diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json
index 9581e207..c69842fc 100644
--- a/frontend/public/components-metadata.json
+++ b/frontend/public/components-metadata.json
@@ -517,6 +517,66 @@
"category": "analog",
"description": "Single-channel optocoupler. CTR 80–600% (typ. 100%). Higher drive than 4N25, commonly used for MCU-to-mains isolation."
},
+ {
+ "thumbnail": "",
+ "tags": [
+ "power-supply",
+ "regulated",
+ "source",
+ "psu",
+ "bench",
+ "lab",
+ "dc",
+ "ac",
+ "analog"
+ ],
+ "properties": [
+ {
+ "name": "mode",
+ "type": "string",
+ "defaultValue": "dc",
+ "control": "select",
+ "options": [
+ "dc",
+ "ac"
+ ],
+ "description": "DC for fixed rails, AC for sine waveform output"
+ },
+ {
+ "name": "voltage",
+ "type": "number",
+ "defaultValue": 5,
+ "control": "text",
+ "description": "Output voltage in V (DC mode) or peak amplitude (AC mode)"
+ },
+ {
+ "name": "frequency",
+ "type": "number",
+ "defaultValue": 50,
+ "control": "text",
+ "description": "Frequency in Hz (AC mode only)"
+ },
+ {
+ "name": "currentLimit",
+ "type": "number",
+ "defaultValue": 1,
+ "control": "text",
+ "description": "Maximum sourced current in A. The verifier warns when exceeded."
+ }
+ ],
+ "defaultValues": {
+ "mode": "dc",
+ "voltage": 5,
+ "frequency": 50,
+ "currentLimit": 1
+ },
+ "pinCount": 2,
+ "id": "power-supply",
+ "tagName": "wokwi-signal-generator",
+ "name": "Regulated Power Supply",
+ "category": "analog",
+ "description": "Bench DC/AC power supply with optional current limit. DC mode for fixed rails; AC mode for sine. Verifier enforces the current limit per instance."
+ },
{
"thumbnail": "",
"tags": [
diff --git a/frontend/src/simulation/spice/componentToSpice.ts b/frontend/src/simulation/spice/componentToSpice.ts
index 95149309..8face639 100644
--- a/frontend/src/simulation/spice/componentToSpice.ts
+++ b/frontend/src/simulation/spice/componentToSpice.ts
@@ -548,6 +548,41 @@ const MAPPERS: Record = {
};
},
+ // ── Regulated power supply (DC / AC, current-limit aware) ───────────────
+ // Properties:
+ // mode: 'dc' | 'ac' (default 'dc')
+ // voltage: V (default 5)
+ // frequency: Hz (default 50, only used in AC mode)
+ // currentLimit: A (default 1; enforced by circuitVerifier,
+ // NOT SPICE — ngspice has no native
+ // foldback limiter)
+ // The visual element reuses wokwi-signal-generator (no new Web Component
+ // needed). ESR is derived from the currentLimit so a near-short reads
+ // as I ≈ 1.5·limit, which the verifier then flags as source-overload.
+ 'power-supply': (comp, netLookup) => {
+ const pos = netLookup('+') ?? netLookup('SIG') ?? netLookup('VCC');
+ const neg = netLookup('−') ?? netLookup('-') ?? netLookup('GND');
+ if (!pos || !neg) return null;
+ const mode = String(comp.properties.mode ?? 'dc').toLowerCase();
+ const voltage = Number(comp.properties.voltage ?? 5);
+ const currentLimit = Math.max(0.01, Number(comp.properties.currentLimit ?? 1));
+ const esr = Math.max(0.01, voltage / (currentLimit * 1.5));
+ let source: string;
+ if (mode === 'ac') {
+ const freq = Number(comp.properties.frequency ?? 50);
+ source = `SIN(0 ${voltage} ${freq})`;
+ } else {
+ source = `DC ${voltage}`;
+ }
+ return {
+ cards: [
+ `V_${comp.id} ${pos} ${comp.id}_int ${source}`,
+ `R_${comp.id}_esr ${comp.id}_int ${neg} ${esr}`,
+ ],
+ modelsUsed: new Set(),
+ };
+ },
+
// ── Signal generator (AC / pulse / DC) ───────────────────────────────────
// Properties:
// waveform: 'sine' | 'square' | 'dc' (default 'sine')
diff --git a/frontend/src/simulation/verify/circuitVerifier.ts b/frontend/src/simulation/verify/circuitVerifier.ts
index acd0ea56..7ce172d6 100644
--- a/frontend/src/simulation/verify/circuitVerifier.ts
+++ b/frontend/src/simulation/verify/circuitVerifier.ts
@@ -128,22 +128,36 @@ export async function verifyCircuit(
const branchCurrents = solve.branchCurrents;
// ── Rule 1: short circuit / power source overload ──────────────────────
- // Every voltage source (battery / signal-generator) emits a branch current
- // `i(v_)`. SPICE convention: V-source's current is measured + → −
- // INTERNALLY, so external current draw is the absolute value.
+ // Every voltage source (battery / signal-generator / power-supply) emits
+ // a branch current `i(v_)`. SPICE convention: V-source's current is
+ // measured + → − INTERNALLY, so external current draw is the absolute
+ // value.
+ //
+ // power-supply components carry a per-instance `currentLimit` property
+ // that overrides the global short-circuit threshold — that matches what
+ // a real bench supply does: a 100mA-limited supply trips at 100mA, a
+ // 5A-limited supply tolerates up to 5A before flagging fault.
const sourceComponents = input.components.filter((c) =>
- /^(battery|signal-generator)/.test(c.metadataId),
+ /^(battery|signal-generator|power-supply)/.test(c.metadataId),
);
for (const src of sourceComponents) {
const i = Math.abs(branchCurrents[`v_${src.id}`] ?? 0);
- if (i >= config.shortCircuitAmps) {
+ const perInstanceLimit =
+ src.metadataId === 'power-supply'
+ ? Number(src.properties?.currentLimit ?? config.shortCircuitAmps)
+ : config.shortCircuitAmps;
+ const threshold = Number.isFinite(perInstanceLimit) && perInstanceLimit > 0
+ ? perInstanceLimit
+ : config.shortCircuitAmps;
+ if (i >= threshold) {
+ const isPsu = src.metadataId === 'power-supply';
errors.push({
severity: 'error',
- code: 'short-circuit',
+ code: isPsu ? 'source-overload' : 'short-circuit',
componentId: src.id,
- message: `Possible short circuit — ${src.metadataId} ${src.id} is delivering ${formatAmps(
- i,
- )} (threshold ${formatAmps(config.shortCircuitAmps)}). Check for 5 V tied directly to GND.`,
+ message: isPsu
+ ? `Power supply ${src.id} is being asked for ${formatAmps(i)} — past its ${formatAmps(threshold)} current limit. A real bench supply would foldback or cut out. Raise the currentLimit or add more series resistance to the load.`
+ : `Possible short circuit — ${src.metadataId} ${src.id} is delivering ${formatAmps(i)} (threshold ${formatAmps(threshold)}). Check for power tied directly to GND.`,
metric: i,
});
}
diff --git a/scripts/component-overrides.json b/scripts/component-overrides.json
index 4761901e..243b24ae 100644
--- a/scripts/component-overrides.json
+++ b/scripts/component-overrides.json
@@ -1044,6 +1044,65 @@
"analog"
]
},
+ {
+ "id": "power-supply",
+ "tagName": "wokwi-signal-generator",
+ "name": "Regulated Power Supply",
+ "category": "analog",
+ "description": "Bench DC/AC power supply with optional current limit. DC mode for fixed rails (5V, 12V, etc.); AC mode for sine sources. Visually shares the signal-generator chassis so no new Web Component is needed. The current limit is enforced by the circuit verifier — if any load pulls more than the configured amperage, Run is blocked with a 'source-overload' warning, mimicking a real bench supply's foldback behavior.",
+ "properties": [
+ {
+ "name": "mode",
+ "type": "string",
+ "defaultValue": "dc",
+ "control": "select",
+ "options": [
+ "dc",
+ "ac"
+ ],
+ "description": "DC for fixed rails, AC for sine waveform output"
+ },
+ {
+ "name": "voltage",
+ "type": "number",
+ "defaultValue": 5,
+ "control": "text",
+ "description": "Output voltage in V (DC mode) or peak amplitude (AC mode)"
+ },
+ {
+ "name": "frequency",
+ "type": "number",
+ "defaultValue": 50,
+ "control": "text",
+ "description": "Frequency in Hz (AC mode only)"
+ },
+ {
+ "name": "currentLimit",
+ "type": "number",
+ "defaultValue": 1,
+ "control": "text",
+ "description": "Maximum sourced current in A. The verifier warns when exceeded."
+ }
+ ],
+ "defaultValues": {
+ "mode": "dc",
+ "voltage": 5,
+ "frequency": 50,
+ "currentLimit": 1
+ },
+ "pinCount": 2,
+ "tags": [
+ "power-supply",
+ "regulated",
+ "source",
+ "psu",
+ "bench",
+ "lab",
+ "dc",
+ "ac",
+ "analog"
+ ]
+ },
{
"id": "signal-generator",
"tagName": "wokwi-signal-generator",