Skip to content

Commit 3f5d162

Browse files
ShiboSoftwareDevYour Name
andauthored
feat(spice): Implement Op-Amp Simulation Support (#34)
Co-authored-by: Your Name <you@example.com>
1 parent 8b7b627 commit 3f5d162

3 files changed

Lines changed: 133 additions & 0 deletions

File tree

lib/circuitJsonToSpice.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SpiceNetlist } from "./spice-classes/SpiceNetlist"
22
import { SpiceComponent } from "./spice-classes/SpiceComponent"
33
import type {
44
AnyCircuitElement,
5+
SimulationOpAmp,
56
SimulationSwitch,
67
SimulationVoltageProbe,
78
SourceSimpleDiode,
@@ -21,6 +22,7 @@ import { processSimpleTransistor } from "./processors/simple-transistor"
2122
import { processSimulationVoltageSources } from "./processors/process-simulation-voltage-sources"
2223
import { processSimulationCurrentSources } from "./processors/process-simulation-current-sources"
2324
import { processSimulationExperiment } from "./processors/process-simulation-experiment"
25+
import { processSimulationOpAmps } from "./processors/process-simulation-op-amp"
2426

2527
export function circuitJsonToSpice(
2628
circuitJson: AnyCircuitElement[],
@@ -37,6 +39,9 @@ export function circuitJsonToSpice(
3739
(element) => (element as { type?: string }).type === "simulation_switch",
3840
)
3941
.map((element) => element as unknown as SimulationSwitch)
42+
const simulationOpAmps = circuitJson.filter(
43+
(elm) => elm.type === "simulation_op_amp",
44+
) as SimulationOpAmp[]
4045
const simulationSwitchMap = new Map<string, SimulationSwitch>()
4146

4247
for (const simSwitch of simulationSwitches) {
@@ -273,6 +278,8 @@ export function circuitJsonToSpice(
273278
nodeMap,
274279
)
275280

281+
processSimulationOpAmps(netlist, simulationOpAmps, nodeMap)
282+
276283
const simulationExperiment = circuitJson.find(
277284
(elm) => elm.type === "simulation_experiment",
278285
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { SimulationOpAmp } from "circuit-json"
2+
import { SpiceComponent } from "lib/spice-classes/SpiceComponent"
3+
import type { SpiceNetlist } from "lib/spice-classes/SpiceNetlist"
4+
import { SubcircuitCallCommand } from "lib/spice-commands"
5+
6+
const OPAMP_SUBCIRCUIT_NAME = "GENERIC_OPAMP"
7+
const OPAMP_SUBCIRCUIT_DEFINITION = `
8+
.SUBCKT ${OPAMP_SUBCIRCUIT_NAME} non_inverting_input inverting_input positive_supply negative_supply output
9+
* Generic Op-Amp Model
10+
E1 internal_output 0 non_inverting_input inverting_input 100k
11+
Rin non_inverting_input inverting_input 10Meg
12+
Rout internal_output output 75
13+
D1 output positive_supply opamp_diode
14+
D2 negative_supply output opamp_diode
15+
.model opamp_diode D
16+
.ENDS ${OPAMP_SUBCIRCUIT_NAME}
17+
`.trim()
18+
19+
export const processSimulationOpAmps = (
20+
netlist: SpiceNetlist,
21+
simulationOpAmps: SimulationOpAmp[],
22+
nodeMap: Map<string, string>,
23+
) => {
24+
if (simulationOpAmps.length === 0) return
25+
26+
if (!netlist.models.has(OPAMP_SUBCIRCUIT_NAME)) {
27+
netlist.models.set(OPAMP_SUBCIRCUIT_NAME, OPAMP_SUBCIRCUIT_DEFINITION)
28+
}
29+
30+
for (const simOpAmp of simulationOpAmps) {
31+
if (simOpAmp.type !== "simulation_op_amp") continue
32+
33+
const nonInvertingInputNode =
34+
nodeMap.get(simOpAmp.non_inverting_input_source_port_id) ?? "0"
35+
const invertingInputNode =
36+
nodeMap.get(simOpAmp.inverting_input_source_port_id) ?? "0"
37+
const outputNode = nodeMap.get(simOpAmp.output_source_port_id) ?? "0"
38+
const positiveSupplyNode =
39+
nodeMap.get(simOpAmp.positive_supply_source_port_id) ?? "0"
40+
const negativeSupplyNode =
41+
nodeMap.get(simOpAmp.negative_supply_source_port_id) ?? "0"
42+
43+
const nodes = [
44+
nonInvertingInputNode,
45+
invertingInputNode,
46+
positiveSupplyNode,
47+
negativeSupplyNode,
48+
outputNode,
49+
]
50+
51+
const opAmpCmd = new SubcircuitCallCommand({
52+
name: simOpAmp.simulation_op_amp_id,
53+
nodes,
54+
subcircuitName: OPAMP_SUBCIRCUIT_NAME,
55+
})
56+
57+
const spiceComponent = new SpiceComponent(
58+
simOpAmp.simulation_op_amp_id,
59+
opAmpCmd,
60+
nodes,
61+
)
62+
netlist.addComponent(spiceComponent)
63+
}
64+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { test, expect } from "bun:test"
2+
import { circuitJsonToSpice } from "lib/circuitJsonToSpice"
3+
import type { AnyCircuitElement } from "circuit-json"
4+
5+
test("simulation op amp", () => {
6+
const circuitJson: AnyCircuitElement[] = [
7+
{
8+
type: "source_port",
9+
source_port_id: "U1_non_inverting_input",
10+
name: "non_inverting_input",
11+
},
12+
{
13+
type: "source_port",
14+
source_port_id: "U1_inverting_input",
15+
name: "inverting_input",
16+
},
17+
{
18+
type: "source_port",
19+
source_port_id: "U1_positive_supply",
20+
name: "positive_supply",
21+
},
22+
{
23+
type: "source_port",
24+
source_port_id: "U1_negative_supply",
25+
name: "negative_supply",
26+
},
27+
{
28+
type: "source_port",
29+
source_port_id: "U1_output",
30+
name: "output",
31+
},
32+
{
33+
type: "simulation_op_amp",
34+
simulation_op_amp_id: "sim_op_amp_1",
35+
inverting_input_source_port_id: "U1_inverting_input",
36+
non_inverting_input_source_port_id: "U1_non_inverting_input",
37+
output_source_port_id: "U1_output",
38+
positive_supply_source_port_id: "U1_positive_supply",
39+
negative_supply_source_port_id: "U1_negative_supply",
40+
},
41+
]
42+
43+
const netlist = circuitJsonToSpice(circuitJson)
44+
const spiceString = netlist.toSpiceString()
45+
46+
expect(spiceString).toContain(".SUBCKT GENERIC_OPAMP")
47+
expect(spiceString).toContain("Xsim_op_amp_1 N1 N2 N3 N4 N5 GENERIC_OPAMP")
48+
expect(spiceString).toMatchInlineSnapshot(`
49+
"* Circuit JSON to SPICE Netlist
50+
.SUBCKT GENERIC_OPAMP non_inverting_input inverting_input positive_supply negative_supply output
51+
* Generic Op-Amp Model
52+
E1 internal_output 0 non_inverting_input inverting_input 100k
53+
Rin non_inverting_input inverting_input 10Meg
54+
Rout internal_output output 75
55+
D1 output positive_supply opamp_diode
56+
D2 negative_supply output opamp_diode
57+
.model opamp_diode D
58+
.ENDS GENERIC_OPAMP
59+
Xsim_op_amp_1 N1 N2 N3 N4 N5 GENERIC_OPAMP
60+
.END"
61+
`)
62+
})

0 commit comments

Comments
 (0)