@tscircuit/ngspice-spice-engine 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +67 -4
- package/dist/index.js +615 -195
- package/package.json +7 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SpiceEngine } from '@tscircuit/props';
|
|
2
|
-
import {
|
|
2
|
+
import { SimulationAcSweepVoltageGraph, SimulationAnalysisResult } from 'circuit-json';
|
|
3
3
|
|
|
4
4
|
interface TranParams {
|
|
5
5
|
tstep?: number;
|
|
@@ -10,6 +10,18 @@ interface TranParams {
|
|
|
10
10
|
}
|
|
11
11
|
declare const parseTranParams: (spiceString: string) => TranParams | null;
|
|
12
12
|
|
|
13
|
+
type SimulationAnalysis = {
|
|
14
|
+
type: "transient";
|
|
15
|
+
} | {
|
|
16
|
+
type: "dc_operating_point";
|
|
17
|
+
} | {
|
|
18
|
+
type: "dc_sweep";
|
|
19
|
+
sweepUnit: "V" | "A";
|
|
20
|
+
} | {
|
|
21
|
+
type: "ac_sweep";
|
|
22
|
+
};
|
|
23
|
+
declare const parseSimulationAnalysis: (spiceString: string) => SimulationAnalysis | null;
|
|
24
|
+
|
|
13
25
|
declare const rewritePspiceCompatibilitySyntax: (spiceString: string) => string;
|
|
14
26
|
|
|
15
27
|
type ComplexNumber = {
|
|
@@ -58,6 +70,7 @@ interface CurrentProbeMetadata {
|
|
|
58
70
|
}
|
|
59
71
|
interface VoltageGraph {
|
|
60
72
|
graphType: "voltage";
|
|
73
|
+
analysisType: "transient";
|
|
61
74
|
netName: string;
|
|
62
75
|
time: number[];
|
|
63
76
|
voltage: number[];
|
|
@@ -65,18 +78,68 @@ interface VoltageGraph {
|
|
|
65
78
|
}
|
|
66
79
|
interface CurrentGraph {
|
|
67
80
|
graphType: "current";
|
|
81
|
+
analysisType: "transient";
|
|
68
82
|
currentName: string;
|
|
69
83
|
time: number[];
|
|
70
84
|
current: number[];
|
|
71
85
|
probeMetadata?: CurrentProbeMetadata;
|
|
72
86
|
}
|
|
73
|
-
|
|
87
|
+
interface DcOperatingPointVoltageResult {
|
|
88
|
+
graphType: "voltage";
|
|
89
|
+
analysisType: "dc_operating_point";
|
|
90
|
+
netName: string;
|
|
91
|
+
voltage: number;
|
|
92
|
+
probeMetadata?: VoltageProbeMetadata;
|
|
93
|
+
}
|
|
94
|
+
interface DcOperatingPointCurrentResult {
|
|
95
|
+
graphType: "current";
|
|
96
|
+
analysisType: "dc_operating_point";
|
|
97
|
+
currentName: string;
|
|
98
|
+
current: number;
|
|
99
|
+
probeMetadata?: CurrentProbeMetadata;
|
|
100
|
+
}
|
|
101
|
+
interface DcSweepVoltageGraph {
|
|
102
|
+
graphType: "voltage";
|
|
103
|
+
analysisType: "dc_sweep";
|
|
104
|
+
netName: string;
|
|
105
|
+
sweepValues: number[];
|
|
106
|
+
sweepUnit: "V" | "A";
|
|
107
|
+
voltage: number[];
|
|
108
|
+
probeMetadata?: VoltageProbeMetadata;
|
|
109
|
+
}
|
|
110
|
+
interface DcSweepCurrentGraph {
|
|
111
|
+
graphType: "current";
|
|
112
|
+
analysisType: "dc_sweep";
|
|
113
|
+
currentName: string;
|
|
114
|
+
sweepValues: number[];
|
|
115
|
+
sweepUnit: "V" | "A";
|
|
116
|
+
current: number[];
|
|
117
|
+
probeMetadata?: CurrentProbeMetadata;
|
|
118
|
+
}
|
|
119
|
+
type ComplexSample = SimulationAcSweepVoltageGraph["complex_voltages"][number];
|
|
120
|
+
interface AcSweepVoltageGraph {
|
|
121
|
+
graphType: "voltage";
|
|
122
|
+
analysisType: "ac_sweep";
|
|
123
|
+
netName: string;
|
|
124
|
+
frequenciesHz: number[];
|
|
125
|
+
complexVoltages: ComplexSample[];
|
|
126
|
+
probeMetadata?: VoltageProbeMetadata;
|
|
127
|
+
}
|
|
128
|
+
interface AcSweepCurrentGraph {
|
|
129
|
+
graphType: "current";
|
|
130
|
+
analysisType: "ac_sweep";
|
|
131
|
+
currentName: string;
|
|
132
|
+
frequenciesHz: number[];
|
|
133
|
+
complexCurrents: ComplexSample[];
|
|
134
|
+
probeMetadata?: CurrentProbeMetadata;
|
|
135
|
+
}
|
|
136
|
+
type SimulationGraph = VoltageGraph | CurrentGraph | DcOperatingPointVoltageResult | DcOperatingPointCurrentResult | DcSweepVoltageGraph | DcSweepCurrentGraph | AcSweepVoltageGraph | AcSweepCurrentGraph;
|
|
74
137
|
|
|
75
138
|
declare const eecircuitResultToSimulationGraphs: (result: ResultType, spiceString: string) => SimulationGraph[];
|
|
76
139
|
declare const eecircuitResultToVGraphs: (result: ResultType, spiceString: string) => VoltageGraph[];
|
|
77
140
|
|
|
78
|
-
declare const simulationGraphsToCircuitJson: (graphs: SimulationGraph[], spiceString: string) =>
|
|
141
|
+
declare const simulationGraphsToCircuitJson: (graphs: SimulationGraph[], spiceString: string) => SimulationAnalysisResult[];
|
|
79
142
|
|
|
80
143
|
declare const createNgspiceSpiceEngine: () => Promise<SpiceEngine>;
|
|
81
144
|
|
|
82
|
-
export { type TranParams, createNgspiceSpiceEngine, createNgspiceSpiceEngine as default, eecircuitResultToSimulationGraphs, eecircuitResultToVGraphs, parseTranParams, rewritePspiceCompatibilitySyntax, simulationGraphsToCircuitJson };
|
|
145
|
+
export { type SimulationAnalysis, type TranParams, createNgspiceSpiceEngine, createNgspiceSpiceEngine as default, eecircuitResultToSimulationGraphs, eecircuitResultToVGraphs, parseSimulationAnalysis, parseTranParams, rewritePspiceCompatibilitySyntax, simulationGraphsToCircuitJson };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var importEecircuitEngine = async () => {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
// lib/parse-tran-params.ts
|
|
23
|
+
import { parseSpiceNetlist, Tran } from "spicets";
|
|
23
24
|
var SUFFIX_MULTIPLIERS = {
|
|
24
25
|
t: 1e12,
|
|
25
26
|
g: 1e9,
|
|
@@ -57,51 +58,33 @@ var parseNumericToken = (token) => {
|
|
|
57
58
|
return base * multiplier;
|
|
58
59
|
};
|
|
59
60
|
var parseTranParams = (spiceString) => {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
if (!line.toLowerCase().startsWith(".tran")) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const [withoutComments = ""] = line.split(";");
|
|
70
|
-
const tokens = withoutComments.split(/\s+/).filter(Boolean);
|
|
71
|
-
if (tokens.length <= 1) {
|
|
72
|
-
return {};
|
|
73
|
-
}
|
|
74
|
-
const values = [];
|
|
75
|
-
let uic = false;
|
|
76
|
-
for (const token of tokens.slice(1)) {
|
|
77
|
-
if (token.toLowerCase() === "uic") {
|
|
78
|
-
uic = true;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const value = parseNumericToken(token);
|
|
82
|
-
if (value !== void 0) {
|
|
83
|
-
values.push(value);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const params = {};
|
|
87
|
-
if (values[0] !== void 0) {
|
|
88
|
-
params.tstep = values[0];
|
|
89
|
-
}
|
|
90
|
-
if (values[1] !== void 0) {
|
|
91
|
-
params.tstop = values[1];
|
|
92
|
-
}
|
|
93
|
-
if (values[2] !== void 0) {
|
|
94
|
-
params.tstart = values[2];
|
|
95
|
-
}
|
|
96
|
-
if (values[3] !== void 0) {
|
|
97
|
-
params.tmax = values[3];
|
|
98
|
-
}
|
|
99
|
-
if (uic) {
|
|
100
|
-
params.uic = true;
|
|
101
|
-
}
|
|
102
|
-
return params;
|
|
61
|
+
const tran = parseSpiceNetlist(spiceString, {
|
|
62
|
+
dialect: "ngspice"
|
|
63
|
+
}).analyses.find((analysis) => analysis instanceof Tran);
|
|
64
|
+
if (!tran) {
|
|
65
|
+
return null;
|
|
103
66
|
}
|
|
104
|
-
|
|
67
|
+
const params = {};
|
|
68
|
+
const tstep = tran.step ? parseNumericToken(tran.step.raw) : void 0;
|
|
69
|
+
const tstop = parseNumericToken(tran.stop.raw);
|
|
70
|
+
const tstart = tran.start ? parseNumericToken(tran.start.raw) : void 0;
|
|
71
|
+
const tmax = tran.maxStep ? parseNumericToken(tran.maxStep.raw) : void 0;
|
|
72
|
+
if (tstep !== void 0) {
|
|
73
|
+
params.tstep = tstep;
|
|
74
|
+
}
|
|
75
|
+
if (tstop !== void 0) {
|
|
76
|
+
params.tstop = tstop;
|
|
77
|
+
}
|
|
78
|
+
if (tstart !== void 0) {
|
|
79
|
+
params.tstart = tstart;
|
|
80
|
+
}
|
|
81
|
+
if (tmax !== void 0) {
|
|
82
|
+
params.tmax = tmax;
|
|
83
|
+
}
|
|
84
|
+
if (tran.uic) {
|
|
85
|
+
params.uic = true;
|
|
86
|
+
}
|
|
87
|
+
return params;
|
|
105
88
|
};
|
|
106
89
|
|
|
107
90
|
// lib/rewrite-pspice-compatibility-syntax.ts
|
|
@@ -170,149 +153,407 @@ var rewritePspiceValueBooleanCarets = (spiceString) => {
|
|
|
170
153
|
var rewritePspiceCompatibilitySyntax = (spiceString) => rewritePspiceValueBooleanCarets(rewritePspiceResistorTcPairs(spiceString));
|
|
171
154
|
|
|
172
155
|
// lib/create-graph-from-requested-plot.ts
|
|
173
|
-
var getNetName = (
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
156
|
+
var getNetName = (spiceVector) => {
|
|
157
|
+
const differentialVoltageMatch = spiceVector.match(
|
|
158
|
+
/^v\(([^,]+),\s*([^)]+)\)$/i
|
|
159
|
+
);
|
|
160
|
+
if (differentialVoltageMatch?.[1] && differentialVoltageMatch?.[2]) {
|
|
161
|
+
return `${differentialVoltageMatch[1].trim()}-${differentialVoltageMatch[2].trim()}`;
|
|
177
162
|
}
|
|
178
|
-
const match =
|
|
163
|
+
const match = spiceVector.match(/^v\((.*)\)$/i);
|
|
179
164
|
if (!match) {
|
|
180
|
-
return
|
|
165
|
+
return spiceVector;
|
|
181
166
|
}
|
|
182
|
-
return match[1] ??
|
|
167
|
+
return match[1] ?? spiceVector;
|
|
183
168
|
};
|
|
184
|
-
var getCurrentName = (
|
|
185
|
-
const match =
|
|
169
|
+
var getCurrentName = (spiceVector) => {
|
|
170
|
+
const match = spiceVector.match(/^i\((.*)\)$/i);
|
|
186
171
|
if (!match) {
|
|
187
|
-
return
|
|
172
|
+
return spiceVector;
|
|
188
173
|
}
|
|
189
|
-
return match[1] ??
|
|
174
|
+
return match[1] ?? spiceVector;
|
|
190
175
|
};
|
|
191
176
|
var createVoltageGraphFromRequestedPlot = ({
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
177
|
+
normalizedSpiceVector,
|
|
178
|
+
originalSpiceVector,
|
|
179
|
+
timeSeconds,
|
|
180
|
+
voltageSamplesBySpiceVector,
|
|
181
|
+
voltageProbeMetadataBySpiceVector
|
|
197
182
|
}) => {
|
|
198
|
-
if (!
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
183
|
+
if (!normalizedSpiceVector.startsWith("v(")) return null;
|
|
184
|
+
const differentialVoltageMatch = originalSpiceVector.match(
|
|
185
|
+
/^v\(([^,]+),\s*([^)]+)\)$/i
|
|
186
|
+
);
|
|
187
|
+
let voltageSamples;
|
|
188
|
+
if (differentialVoltageMatch?.[1] && differentialVoltageMatch?.[2]) {
|
|
189
|
+
voltageSamples = voltageSamplesBySpiceVector.get(normalizedSpiceVector);
|
|
190
|
+
if (!voltageSamples) {
|
|
191
|
+
const positiveNodeName = differentialVoltageMatch[1].trim();
|
|
192
|
+
const referenceNodeName = differentialVoltageMatch[2].trim();
|
|
193
|
+
const positiveVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
194
|
+
`v(${positiveNodeName.toLowerCase()})`
|
|
195
|
+
);
|
|
196
|
+
const referenceVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
197
|
+
`v(${referenceNodeName.toLowerCase()})`
|
|
198
|
+
);
|
|
199
|
+
if (positiveVoltageSamples && referenceVoltageSamples) {
|
|
200
|
+
voltageSamples = positiveVoltageSamples.map(
|
|
201
|
+
(positiveVoltage, sampleIndex) => positiveVoltage - (referenceVoltageSamples[sampleIndex] ?? 0)
|
|
202
|
+
);
|
|
210
203
|
}
|
|
211
204
|
}
|
|
212
205
|
} else {
|
|
213
|
-
|
|
206
|
+
voltageSamples = voltageSamplesBySpiceVector.get(normalizedSpiceVector);
|
|
214
207
|
}
|
|
215
|
-
if (!
|
|
216
|
-
const
|
|
208
|
+
if (!voltageSamples) return null;
|
|
209
|
+
const probeMetadata = voltageProbeMetadataBySpiceVector.get(
|
|
210
|
+
normalizedSpiceVector
|
|
211
|
+
);
|
|
217
212
|
const voltageGraph = {
|
|
218
213
|
graphType: "voltage",
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
214
|
+
analysisType: "transient",
|
|
215
|
+
netName: probeMetadata?.name ?? getNetName(originalSpiceVector),
|
|
216
|
+
time: timeSeconds,
|
|
217
|
+
voltage: voltageSamples,
|
|
218
|
+
probeMetadata
|
|
223
219
|
};
|
|
224
220
|
return voltageGraph;
|
|
225
221
|
};
|
|
226
222
|
var createCurrentGraphFromRequestedPlot = ({
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
223
|
+
normalizedSpiceVector,
|
|
224
|
+
originalSpiceVector,
|
|
225
|
+
timeSeconds,
|
|
226
|
+
currentSamplesBySpiceVector,
|
|
227
|
+
currentProbeMetadataBySpiceVector
|
|
232
228
|
}) => {
|
|
233
|
-
if (!
|
|
234
|
-
const
|
|
235
|
-
if (!
|
|
236
|
-
const
|
|
229
|
+
if (!normalizedSpiceVector.startsWith("i(")) return null;
|
|
230
|
+
const currentSamples = currentSamplesBySpiceVector.get(normalizedSpiceVector);
|
|
231
|
+
if (!currentSamples) return null;
|
|
232
|
+
const probeMetadata = currentProbeMetadataBySpiceVector.get(
|
|
233
|
+
normalizedSpiceVector
|
|
234
|
+
);
|
|
237
235
|
const currentGraph = {
|
|
238
236
|
graphType: "current",
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
237
|
+
analysisType: "transient",
|
|
238
|
+
currentName: probeMetadata?.name ?? getCurrentName(originalSpiceVector),
|
|
239
|
+
time: timeSeconds,
|
|
240
|
+
current: currentSamples,
|
|
241
|
+
probeMetadata
|
|
243
242
|
};
|
|
244
243
|
return currentGraph;
|
|
245
244
|
};
|
|
246
245
|
|
|
247
246
|
// lib/extract-probe-metadata.ts
|
|
248
|
-
var normalizeSpiceVector = (
|
|
247
|
+
var normalizeSpiceVector = (spiceVector) => spiceVector.toLowerCase().replace(/\s/g, "");
|
|
249
248
|
var extractVoltageProbeMetadata = (spiceString) => {
|
|
250
|
-
const
|
|
249
|
+
const voltageProbeMetadataBySpiceVector = /* @__PURE__ */ new Map();
|
|
251
250
|
for (const line of spiceString.split(/\r?\n/)) {
|
|
252
251
|
const match = line.match(/^\s*\*\s*tscircuit_probe\s+(.+)\s*$/);
|
|
253
252
|
if (!match?.[1]) continue;
|
|
254
253
|
try {
|
|
255
|
-
const
|
|
256
|
-
if (typeof
|
|
254
|
+
const parsedProbeMetadata = JSON.parse(match[1]);
|
|
255
|
+
if (typeof parsedProbeMetadata !== "object" || parsedProbeMetadata === null || !("simulation_voltage_probe_id" in parsedProbeMetadata) || typeof parsedProbeMetadata.simulation_voltage_probe_id !== "string" || !("spice_vector" in parsedProbeMetadata) || typeof parsedProbeMetadata.spice_vector !== "string" || !("source_node_name" in parsedProbeMetadata) || typeof parsedProbeMetadata.source_node_name !== "string") {
|
|
257
256
|
continue;
|
|
258
257
|
}
|
|
259
|
-
const normalizedSpiceVector = normalizeSpiceVector(
|
|
258
|
+
const normalizedSpiceVector = normalizeSpiceVector(
|
|
259
|
+
parsedProbeMetadata.spice_vector
|
|
260
|
+
);
|
|
260
261
|
const voltageProbeMetadata = {
|
|
261
|
-
simulation_voltage_probe_id:
|
|
262
|
-
name: typeof
|
|
263
|
-
spice_vector:
|
|
264
|
-
source_node_name:
|
|
265
|
-
reference_node_name: typeof
|
|
262
|
+
simulation_voltage_probe_id: parsedProbeMetadata.simulation_voltage_probe_id,
|
|
263
|
+
name: "name" in parsedProbeMetadata && typeof parsedProbeMetadata.name === "string" ? parsedProbeMetadata.name : void 0,
|
|
264
|
+
spice_vector: parsedProbeMetadata.spice_vector,
|
|
265
|
+
source_node_name: parsedProbeMetadata.source_node_name,
|
|
266
|
+
reference_node_name: "reference_node_name" in parsedProbeMetadata && typeof parsedProbeMetadata.reference_node_name === "string" ? parsedProbeMetadata.reference_node_name : void 0
|
|
266
267
|
};
|
|
267
|
-
|
|
268
|
+
voltageProbeMetadataBySpiceVector.set(
|
|
269
|
+
normalizedSpiceVector,
|
|
270
|
+
voltageProbeMetadata
|
|
271
|
+
);
|
|
268
272
|
} catch {
|
|
269
273
|
}
|
|
270
274
|
}
|
|
271
|
-
return
|
|
275
|
+
return voltageProbeMetadataBySpiceVector;
|
|
272
276
|
};
|
|
273
277
|
var extractCurrentProbeMetadata = (spiceString) => {
|
|
274
|
-
const
|
|
278
|
+
const currentProbeMetadataBySpiceVector = /* @__PURE__ */ new Map();
|
|
275
279
|
for (const line of spiceString.split(/\r?\n/)) {
|
|
276
280
|
const match = line.match(/^\s*\*\s*tscircuit_current_probe\s+(.+)\s*$/);
|
|
277
281
|
if (!match?.[1]) continue;
|
|
278
282
|
try {
|
|
279
|
-
const
|
|
280
|
-
if (typeof
|
|
283
|
+
const parsedProbeMetadata = JSON.parse(match[1]);
|
|
284
|
+
if (typeof parsedProbeMetadata !== "object" || parsedProbeMetadata === null || !("simulation_current_probe_id" in parsedProbeMetadata) || typeof parsedProbeMetadata.simulation_current_probe_id !== "string" || !("spice_vector" in parsedProbeMetadata) || typeof parsedProbeMetadata.spice_vector !== "string") {
|
|
281
285
|
continue;
|
|
282
286
|
}
|
|
283
|
-
const normalizedSpiceVector = normalizeSpiceVector(
|
|
287
|
+
const normalizedSpiceVector = normalizeSpiceVector(
|
|
288
|
+
parsedProbeMetadata.spice_vector
|
|
289
|
+
);
|
|
284
290
|
const currentProbeMetadata = {
|
|
285
|
-
simulation_current_probe_id:
|
|
286
|
-
name: typeof
|
|
287
|
-
spice_vector:
|
|
288
|
-
source_component_id: typeof
|
|
289
|
-
source_trace_id: typeof
|
|
291
|
+
simulation_current_probe_id: parsedProbeMetadata.simulation_current_probe_id,
|
|
292
|
+
name: "name" in parsedProbeMetadata && typeof parsedProbeMetadata.name === "string" ? parsedProbeMetadata.name : void 0,
|
|
293
|
+
spice_vector: parsedProbeMetadata.spice_vector,
|
|
294
|
+
source_component_id: "source_component_id" in parsedProbeMetadata && typeof parsedProbeMetadata.source_component_id === "string" ? parsedProbeMetadata.source_component_id : void 0,
|
|
295
|
+
source_trace_id: "source_trace_id" in parsedProbeMetadata && typeof parsedProbeMetadata.source_trace_id === "string" ? parsedProbeMetadata.source_trace_id : void 0
|
|
290
296
|
};
|
|
291
|
-
|
|
297
|
+
currentProbeMetadataBySpiceVector.set(
|
|
298
|
+
normalizedSpiceVector,
|
|
299
|
+
currentProbeMetadata
|
|
300
|
+
);
|
|
292
301
|
} catch {
|
|
293
302
|
}
|
|
294
303
|
}
|
|
295
|
-
return
|
|
304
|
+
return currentProbeMetadataBySpiceVector;
|
|
296
305
|
};
|
|
297
306
|
|
|
298
307
|
// lib/extract-requested-plots.ts
|
|
308
|
+
import { Print, parseSpiceNetlist as parseSpiceNetlist2 } from "spicets";
|
|
299
309
|
var extractRequestedPlots = (spiceString) => {
|
|
300
|
-
const
|
|
301
|
-
|
|
310
|
+
const supportedAnalyses = /* @__PURE__ */ new Set(["tran", "op", "dc", "ac"]);
|
|
311
|
+
const print = parseSpiceNetlist2(spiceString, {
|
|
312
|
+
dialect: "ngspice"
|
|
313
|
+
}).directives.find(
|
|
314
|
+
(directive) => directive instanceof Print && supportedAnalyses.has(directive.analysis?.toLowerCase() ?? "")
|
|
315
|
+
);
|
|
316
|
+
if (!(print instanceof Print)) {
|
|
302
317
|
return null;
|
|
303
318
|
}
|
|
304
|
-
const tokens =
|
|
319
|
+
const tokens = print.getString().match(/[VI]\s*\([^)]+\)/gi);
|
|
305
320
|
if (!tokens) {
|
|
306
321
|
return null;
|
|
307
322
|
}
|
|
308
|
-
const
|
|
323
|
+
const requestedPlotByNormalizedSpiceVector = /* @__PURE__ */ new Map();
|
|
309
324
|
for (const token of tokens) {
|
|
310
|
-
const
|
|
311
|
-
if (!
|
|
312
|
-
|
|
325
|
+
const normalizedSpiceVector = token.toLowerCase().replace(/\s/g, "");
|
|
326
|
+
if (!requestedPlotByNormalizedSpiceVector.has(normalizedSpiceVector)) {
|
|
327
|
+
requestedPlotByNormalizedSpiceVector.set(normalizedSpiceVector, token);
|
|
313
328
|
}
|
|
314
329
|
}
|
|
315
|
-
return
|
|
330
|
+
return requestedPlotByNormalizedSpiceVector;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// lib/non-transient-result-helpers.ts
|
|
334
|
+
var getRequestedPlotByNormalizedSpiceVector = ({
|
|
335
|
+
spiceString,
|
|
336
|
+
simulationOutputs
|
|
337
|
+
}) => {
|
|
338
|
+
const requestedPlotByNormalizedSpiceVector = extractRequestedPlots(spiceString);
|
|
339
|
+
if (requestedPlotByNormalizedSpiceVector) {
|
|
340
|
+
return requestedPlotByNormalizedSpiceVector;
|
|
341
|
+
}
|
|
342
|
+
return new Map(
|
|
343
|
+
simulationOutputs.filter(
|
|
344
|
+
(simulationOutput) => simulationOutput.type === "voltage" || simulationOutput.type === "current"
|
|
345
|
+
).map((simulationOutput) => [
|
|
346
|
+
normalizeSpiceVector(simulationOutput.name),
|
|
347
|
+
simulationOutput.name
|
|
348
|
+
])
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
var getRealSampleMaps = (simulationOutputs) => {
|
|
352
|
+
const voltageSamplesBySpiceVector = new Map(
|
|
353
|
+
simulationOutputs.filter((simulationOutput) => simulationOutput.type === "voltage").map((simulationOutput) => [
|
|
354
|
+
normalizeSpiceVector(simulationOutput.name),
|
|
355
|
+
simulationOutput.values
|
|
356
|
+
])
|
|
357
|
+
);
|
|
358
|
+
const currentSamplesBySpiceVector = new Map(
|
|
359
|
+
simulationOutputs.filter((simulationOutput) => simulationOutput.type === "current").map((simulationOutput) => [
|
|
360
|
+
normalizeSpiceVector(simulationOutput.name),
|
|
361
|
+
simulationOutput.values
|
|
362
|
+
])
|
|
363
|
+
);
|
|
364
|
+
return { voltageSamplesBySpiceVector, currentSamplesBySpiceVector };
|
|
365
|
+
};
|
|
366
|
+
var getComplexSampleMaps = (simulationOutputs) => {
|
|
367
|
+
const voltageSamplesBySpiceVector = new Map(
|
|
368
|
+
simulationOutputs.filter((simulationOutput) => simulationOutput.type === "voltage").map((simulationOutput) => [
|
|
369
|
+
normalizeSpiceVector(simulationOutput.name),
|
|
370
|
+
simulationOutput.values
|
|
371
|
+
])
|
|
372
|
+
);
|
|
373
|
+
const currentSamplesBySpiceVector = new Map(
|
|
374
|
+
simulationOutputs.filter((simulationOutput) => simulationOutput.type === "current").map((simulationOutput) => [
|
|
375
|
+
normalizeSpiceVector(simulationOutput.name),
|
|
376
|
+
simulationOutput.values
|
|
377
|
+
])
|
|
378
|
+
);
|
|
379
|
+
return { voltageSamplesBySpiceVector, currentSamplesBySpiceVector };
|
|
380
|
+
};
|
|
381
|
+
var getRealVoltageSamples = ({
|
|
382
|
+
normalizedSpiceVector,
|
|
383
|
+
originalSpiceVector,
|
|
384
|
+
voltageSamplesBySpiceVector
|
|
385
|
+
}) => {
|
|
386
|
+
const directVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
387
|
+
normalizedSpiceVector
|
|
388
|
+
);
|
|
389
|
+
if (directVoltageSamples) return directVoltageSamples;
|
|
390
|
+
const differentialVoltageMatch = originalSpiceVector.match(
|
|
391
|
+
/^v\(([^,]+),\s*([^)]+)\)$/i
|
|
392
|
+
);
|
|
393
|
+
if (!differentialVoltageMatch?.[1] || !differentialVoltageMatch[2]) {
|
|
394
|
+
return void 0;
|
|
395
|
+
}
|
|
396
|
+
const positiveVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
397
|
+
normalizeSpiceVector(`v(${differentialVoltageMatch[1]})`)
|
|
398
|
+
);
|
|
399
|
+
const negativeVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
400
|
+
normalizeSpiceVector(`v(${differentialVoltageMatch[2]})`)
|
|
401
|
+
);
|
|
402
|
+
if (!positiveVoltageSamples || !negativeVoltageSamples) return void 0;
|
|
403
|
+
return positiveVoltageSamples.map(
|
|
404
|
+
(positiveVoltage, sampleIndex) => positiveVoltage - (negativeVoltageSamples[sampleIndex] ?? 0)
|
|
405
|
+
);
|
|
406
|
+
};
|
|
407
|
+
var getComplexVoltageSamples = ({
|
|
408
|
+
normalizedSpiceVector,
|
|
409
|
+
originalSpiceVector,
|
|
410
|
+
voltageSamplesBySpiceVector
|
|
411
|
+
}) => {
|
|
412
|
+
const directVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
413
|
+
normalizedSpiceVector
|
|
414
|
+
);
|
|
415
|
+
if (directVoltageSamples) return directVoltageSamples;
|
|
416
|
+
const differentialVoltageMatch = originalSpiceVector.match(
|
|
417
|
+
/^v\(([^,]+),\s*([^)]+)\)$/i
|
|
418
|
+
);
|
|
419
|
+
if (!differentialVoltageMatch?.[1] || !differentialVoltageMatch[2]) {
|
|
420
|
+
return void 0;
|
|
421
|
+
}
|
|
422
|
+
const positiveVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
423
|
+
normalizeSpiceVector(`v(${differentialVoltageMatch[1]})`)
|
|
424
|
+
);
|
|
425
|
+
const negativeVoltageSamples = voltageSamplesBySpiceVector.get(
|
|
426
|
+
normalizeSpiceVector(`v(${differentialVoltageMatch[2]})`)
|
|
427
|
+
);
|
|
428
|
+
if (!positiveVoltageSamples || !negativeVoltageSamples) return void 0;
|
|
429
|
+
return positiveVoltageSamples.map((positiveVoltage, sampleIndex) => ({
|
|
430
|
+
real: positiveVoltage.real - (negativeVoltageSamples[sampleIndex]?.real ?? 0),
|
|
431
|
+
img: positiveVoltage.img - (negativeVoltageSamples[sampleIndex]?.img ?? 0)
|
|
432
|
+
}));
|
|
433
|
+
};
|
|
434
|
+
var toCircuitJsonComplexSamples = (complexSamples) => complexSamples.map(({ real, img }) => ({ re: real, im: img }));
|
|
435
|
+
|
|
436
|
+
// lib/ac-sweep-result-to-graphs.ts
|
|
437
|
+
var acSweepResultToGraphs = ({
|
|
438
|
+
eecircuitResult,
|
|
439
|
+
spiceString
|
|
440
|
+
}) => {
|
|
441
|
+
const frequencyOutput = eecircuitResult.data.find(
|
|
442
|
+
(simulationOutput) => simulationOutput.type === "frequency"
|
|
443
|
+
);
|
|
444
|
+
if (!frequencyOutput) return [];
|
|
445
|
+
const simulationOutputs = eecircuitResult.data.filter(
|
|
446
|
+
(simulationOutput) => simulationOutput !== frequencyOutput
|
|
447
|
+
);
|
|
448
|
+
const { voltageSamplesBySpiceVector, currentSamplesBySpiceVector } = getComplexSampleMaps(simulationOutputs);
|
|
449
|
+
const frequenciesHz = frequencyOutput.values.map(({ real }) => real);
|
|
450
|
+
const voltageProbeMetadataBySpiceVector = extractVoltageProbeMetadata(spiceString);
|
|
451
|
+
const currentProbeMetadataBySpiceVector = extractCurrentProbeMetadata(spiceString);
|
|
452
|
+
const simulationGraphs = [];
|
|
453
|
+
for (const [
|
|
454
|
+
normalizedSpiceVector,
|
|
455
|
+
originalSpiceVector
|
|
456
|
+
] of getRequestedPlotByNormalizedSpiceVector({
|
|
457
|
+
spiceString,
|
|
458
|
+
simulationOutputs
|
|
459
|
+
})) {
|
|
460
|
+
if (normalizedSpiceVector.startsWith("v(")) {
|
|
461
|
+
const complexVoltageSamples = getComplexVoltageSamples({
|
|
462
|
+
normalizedSpiceVector,
|
|
463
|
+
originalSpiceVector,
|
|
464
|
+
voltageSamplesBySpiceVector
|
|
465
|
+
});
|
|
466
|
+
if (!complexVoltageSamples) continue;
|
|
467
|
+
const probeMetadata2 = voltageProbeMetadataBySpiceVector.get(
|
|
468
|
+
normalizedSpiceVector
|
|
469
|
+
);
|
|
470
|
+
simulationGraphs.push({
|
|
471
|
+
graphType: "voltage",
|
|
472
|
+
analysisType: "ac_sweep",
|
|
473
|
+
netName: probeMetadata2?.name ?? getNetName(originalSpiceVector),
|
|
474
|
+
frequenciesHz,
|
|
475
|
+
complexVoltages: toCircuitJsonComplexSamples(complexVoltageSamples),
|
|
476
|
+
probeMetadata: probeMetadata2
|
|
477
|
+
});
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const complexCurrentSamples = currentSamplesBySpiceVector.get(
|
|
481
|
+
normalizedSpiceVector
|
|
482
|
+
);
|
|
483
|
+
if (!complexCurrentSamples) continue;
|
|
484
|
+
const probeMetadata = currentProbeMetadataBySpiceVector.get(
|
|
485
|
+
normalizedSpiceVector
|
|
486
|
+
);
|
|
487
|
+
simulationGraphs.push({
|
|
488
|
+
graphType: "current",
|
|
489
|
+
analysisType: "ac_sweep",
|
|
490
|
+
currentName: probeMetadata?.name ?? getCurrentName(originalSpiceVector),
|
|
491
|
+
frequenciesHz,
|
|
492
|
+
complexCurrents: toCircuitJsonComplexSamples(complexCurrentSamples),
|
|
493
|
+
probeMetadata
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
return simulationGraphs;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// lib/dc-sweep-result-to-graphs.ts
|
|
500
|
+
var dcSweepResultToGraphs = ({
|
|
501
|
+
eecircuitResult,
|
|
502
|
+
spiceString,
|
|
503
|
+
sweepUnit
|
|
504
|
+
}) => {
|
|
505
|
+
const [sweepCoordinateOutput, ...simulationOutputs] = eecircuitResult.data;
|
|
506
|
+
if (!sweepCoordinateOutput) return [];
|
|
507
|
+
const { voltageSamplesBySpiceVector, currentSamplesBySpiceVector } = getRealSampleMaps(simulationOutputs);
|
|
508
|
+
const voltageProbeMetadataBySpiceVector = extractVoltageProbeMetadata(spiceString);
|
|
509
|
+
const currentProbeMetadataBySpiceVector = extractCurrentProbeMetadata(spiceString);
|
|
510
|
+
const simulationGraphs = [];
|
|
511
|
+
for (const [
|
|
512
|
+
normalizedSpiceVector,
|
|
513
|
+
originalSpiceVector
|
|
514
|
+
] of getRequestedPlotByNormalizedSpiceVector({
|
|
515
|
+
spiceString,
|
|
516
|
+
simulationOutputs
|
|
517
|
+
})) {
|
|
518
|
+
if (normalizedSpiceVector.startsWith("v(")) {
|
|
519
|
+
const voltageSamples = getRealVoltageSamples({
|
|
520
|
+
normalizedSpiceVector,
|
|
521
|
+
originalSpiceVector,
|
|
522
|
+
voltageSamplesBySpiceVector
|
|
523
|
+
});
|
|
524
|
+
if (!voltageSamples) continue;
|
|
525
|
+
const probeMetadata2 = voltageProbeMetadataBySpiceVector.get(
|
|
526
|
+
normalizedSpiceVector
|
|
527
|
+
);
|
|
528
|
+
simulationGraphs.push({
|
|
529
|
+
graphType: "voltage",
|
|
530
|
+
analysisType: "dc_sweep",
|
|
531
|
+
netName: probeMetadata2?.name ?? getNetName(originalSpiceVector),
|
|
532
|
+
sweepValues: sweepCoordinateOutput.values,
|
|
533
|
+
sweepUnit,
|
|
534
|
+
voltage: voltageSamples,
|
|
535
|
+
probeMetadata: probeMetadata2
|
|
536
|
+
});
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const currentSamples = currentSamplesBySpiceVector.get(
|
|
540
|
+
normalizedSpiceVector
|
|
541
|
+
);
|
|
542
|
+
if (!currentSamples) continue;
|
|
543
|
+
const probeMetadata = currentProbeMetadataBySpiceVector.get(
|
|
544
|
+
normalizedSpiceVector
|
|
545
|
+
);
|
|
546
|
+
simulationGraphs.push({
|
|
547
|
+
graphType: "current",
|
|
548
|
+
analysisType: "dc_sweep",
|
|
549
|
+
currentName: probeMetadata?.name ?? getCurrentName(originalSpiceVector),
|
|
550
|
+
sweepValues: sweepCoordinateOutput.values,
|
|
551
|
+
sweepUnit,
|
|
552
|
+
current: currentSamples,
|
|
553
|
+
probeMetadata
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
return simulationGraphs;
|
|
316
557
|
};
|
|
317
558
|
|
|
318
559
|
// lib/linear-interpolate.ts
|
|
@@ -340,77 +581,182 @@ var linearInterpolate = (targetX, xPoints, yPoints) => {
|
|
|
340
581
|
return y1 + (y2 - y1) * (targetX - x1) / (x2 - x1);
|
|
341
582
|
};
|
|
342
583
|
|
|
584
|
+
// lib/operating-point-result-to-graphs.ts
|
|
585
|
+
var operatingPointResultToGraphs = ({
|
|
586
|
+
eecircuitResult,
|
|
587
|
+
spiceString
|
|
588
|
+
}) => {
|
|
589
|
+
const simulationOutputs = eecircuitResult.data;
|
|
590
|
+
const { voltageSamplesBySpiceVector, currentSamplesBySpiceVector } = getRealSampleMaps(simulationOutputs);
|
|
591
|
+
const voltageProbeMetadataBySpiceVector = extractVoltageProbeMetadata(spiceString);
|
|
592
|
+
const currentProbeMetadataBySpiceVector = extractCurrentProbeMetadata(spiceString);
|
|
593
|
+
const simulationGraphs = [];
|
|
594
|
+
for (const [
|
|
595
|
+
normalizedSpiceVector,
|
|
596
|
+
originalSpiceVector
|
|
597
|
+
] of getRequestedPlotByNormalizedSpiceVector({
|
|
598
|
+
spiceString,
|
|
599
|
+
simulationOutputs
|
|
600
|
+
})) {
|
|
601
|
+
if (normalizedSpiceVector.startsWith("v(")) {
|
|
602
|
+
const voltage = getRealVoltageSamples({
|
|
603
|
+
normalizedSpiceVector,
|
|
604
|
+
originalSpiceVector,
|
|
605
|
+
voltageSamplesBySpiceVector
|
|
606
|
+
})?.[0];
|
|
607
|
+
if (voltage === void 0) continue;
|
|
608
|
+
const probeMetadata2 = voltageProbeMetadataBySpiceVector.get(
|
|
609
|
+
normalizedSpiceVector
|
|
610
|
+
);
|
|
611
|
+
simulationGraphs.push({
|
|
612
|
+
graphType: "voltage",
|
|
613
|
+
analysisType: "dc_operating_point",
|
|
614
|
+
netName: probeMetadata2?.name ?? getNetName(originalSpiceVector),
|
|
615
|
+
voltage,
|
|
616
|
+
probeMetadata: probeMetadata2
|
|
617
|
+
});
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const current = currentSamplesBySpiceVector.get(normalizedSpiceVector)?.[0];
|
|
621
|
+
if (current === void 0) continue;
|
|
622
|
+
const probeMetadata = currentProbeMetadataBySpiceVector.get(
|
|
623
|
+
normalizedSpiceVector
|
|
624
|
+
);
|
|
625
|
+
simulationGraphs.push({
|
|
626
|
+
graphType: "current",
|
|
627
|
+
analysisType: "dc_operating_point",
|
|
628
|
+
currentName: probeMetadata?.name ?? getCurrentName(originalSpiceVector),
|
|
629
|
+
current,
|
|
630
|
+
probeMetadata
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
return simulationGraphs;
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// lib/parse-simulation-analysis.ts
|
|
637
|
+
import { Ac, Dc, Op, parseSpiceNetlist as parseSpiceNetlist3, Tran as Tran2 } from "spicets";
|
|
638
|
+
var parseSimulationAnalysis = (spiceString) => {
|
|
639
|
+
const analyses = parseSpiceNetlist3(spiceString, {
|
|
640
|
+
dialect: "ngspice"
|
|
641
|
+
}).analyses;
|
|
642
|
+
if (analyses.some((analysis) => analysis instanceof Tran2)) {
|
|
643
|
+
return { type: "transient" };
|
|
644
|
+
}
|
|
645
|
+
if (analyses.some((analysis) => analysis instanceof Op)) {
|
|
646
|
+
return { type: "dc_operating_point" };
|
|
647
|
+
}
|
|
648
|
+
const dcSweep = analyses.find((analysis) => analysis instanceof Dc);
|
|
649
|
+
if (dcSweep) {
|
|
650
|
+
const source = dcSweep.sweeps[0]?.source;
|
|
651
|
+
return {
|
|
652
|
+
type: "dc_sweep",
|
|
653
|
+
sweepUnit: source?.toLowerCase().startsWith("i") ? "A" : "V"
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (analyses.some((analysis) => analysis instanceof Ac)) {
|
|
657
|
+
return { type: "ac_sweep" };
|
|
658
|
+
}
|
|
659
|
+
return null;
|
|
660
|
+
};
|
|
661
|
+
|
|
343
662
|
// lib/eecircuit-result-to-simulation-graphs.ts
|
|
344
663
|
var eecircuitResultToSimulationGraphs = (result, spiceString) => {
|
|
664
|
+
const analysis = parseSimulationAnalysis(spiceString);
|
|
665
|
+
if (analysis?.type === "dc_operating_point") {
|
|
666
|
+
return result.dataType === "real" ? operatingPointResultToGraphs({ eecircuitResult: result, spiceString }) : [];
|
|
667
|
+
}
|
|
668
|
+
if (analysis?.type === "dc_sweep") {
|
|
669
|
+
return result.dataType === "real" ? dcSweepResultToGraphs({
|
|
670
|
+
eecircuitResult: result,
|
|
671
|
+
spiceString,
|
|
672
|
+
sweepUnit: analysis.sweepUnit
|
|
673
|
+
}) : [];
|
|
674
|
+
}
|
|
675
|
+
if (analysis?.type === "ac_sweep") {
|
|
676
|
+
return result.dataType === "complex" ? acSweepResultToGraphs({ eecircuitResult: result, spiceString }) : [];
|
|
677
|
+
}
|
|
345
678
|
if (!result?.data || result.dataType !== "real") {
|
|
346
679
|
return [];
|
|
347
680
|
}
|
|
348
|
-
const
|
|
349
|
-
|
|
681
|
+
const timeOutput = result.data.find(
|
|
682
|
+
(simulationOutput) => simulationOutput.type === "time"
|
|
683
|
+
);
|
|
684
|
+
if (!timeOutput || !Array.isArray(timeOutput.values)) {
|
|
350
685
|
return [];
|
|
351
686
|
}
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
(
|
|
687
|
+
const timeSeconds = timeOutput.values;
|
|
688
|
+
const voltageOutputs = result.data.filter(
|
|
689
|
+
(simulationOutput) => simulationOutput.type === "voltage" && Array.isArray(simulationOutput.values)
|
|
355
690
|
);
|
|
356
|
-
const
|
|
357
|
-
for (const
|
|
358
|
-
|
|
691
|
+
const voltageSamplesBySpiceVector = /* @__PURE__ */ new Map();
|
|
692
|
+
for (const voltageOutput of voltageOutputs) {
|
|
693
|
+
voltageSamplesBySpiceVector.set(
|
|
694
|
+
normalizeSpiceVector(voltageOutput.name),
|
|
695
|
+
voltageOutput.values
|
|
696
|
+
);
|
|
359
697
|
}
|
|
360
|
-
const
|
|
361
|
-
(
|
|
698
|
+
const currentOutputs = result.data.filter(
|
|
699
|
+
(simulationOutput) => simulationOutput.type === "current" && Array.isArray(simulationOutput.values)
|
|
362
700
|
);
|
|
363
|
-
const
|
|
364
|
-
for (const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
701
|
+
const currentSamplesBySpiceVector = /* @__PURE__ */ new Map();
|
|
702
|
+
for (const currentOutput of currentOutputs) {
|
|
703
|
+
currentSamplesBySpiceVector.set(
|
|
704
|
+
normalizeSpiceVector(currentOutput.name),
|
|
705
|
+
currentOutput.values
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
const requestedPlotByNormalizedSpiceVector = extractRequestedPlots(spiceString);
|
|
709
|
+
const voltageProbeMetadataBySpiceVector = extractVoltageProbeMetadata(spiceString);
|
|
710
|
+
const currentProbeMetadataBySpiceVector = extractCurrentProbeMetadata(spiceString);
|
|
711
|
+
if (!requestedPlotByNormalizedSpiceVector) {
|
|
371
712
|
return [
|
|
372
|
-
...
|
|
373
|
-
const
|
|
374
|
-
normalizeSpiceVector(
|
|
713
|
+
...voltageOutputs.map((voltageOutput) => {
|
|
714
|
+
const probeMetadata = voltageProbeMetadataBySpiceVector.get(
|
|
715
|
+
normalizeSpiceVector(voltageOutput.name)
|
|
375
716
|
);
|
|
376
717
|
const voltageGraph = {
|
|
377
718
|
graphType: "voltage",
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
719
|
+
analysisType: "transient",
|
|
720
|
+
netName: probeMetadata?.name ?? getNetName(voltageOutput.name),
|
|
721
|
+
time: timeSeconds,
|
|
722
|
+
voltage: voltageOutput.values,
|
|
723
|
+
probeMetadata
|
|
382
724
|
};
|
|
383
725
|
return voltageGraph;
|
|
384
726
|
}),
|
|
385
|
-
...
|
|
386
|
-
const
|
|
387
|
-
normalizeSpiceVector(
|
|
727
|
+
...currentOutputs.map((currentOutput) => {
|
|
728
|
+
const probeMetadata = currentProbeMetadataBySpiceVector.get(
|
|
729
|
+
normalizeSpiceVector(currentOutput.name)
|
|
388
730
|
);
|
|
389
731
|
const currentGraph = {
|
|
390
732
|
graphType: "current",
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
733
|
+
analysisType: "transient",
|
|
734
|
+
currentName: probeMetadata?.name ?? getCurrentName(currentOutput.name),
|
|
735
|
+
time: timeSeconds,
|
|
736
|
+
current: currentOutput.values,
|
|
737
|
+
probeMetadata
|
|
395
738
|
};
|
|
396
739
|
return currentGraph;
|
|
397
740
|
})
|
|
398
741
|
];
|
|
399
742
|
}
|
|
400
743
|
const graphs = [];
|
|
401
|
-
for (const [
|
|
744
|
+
for (const [
|
|
745
|
+
normalizedSpiceVector,
|
|
746
|
+
originalSpiceVector
|
|
747
|
+
] of requestedPlotByNormalizedSpiceVector) {
|
|
402
748
|
const graph = createVoltageGraphFromRequestedPlot({
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
749
|
+
normalizedSpiceVector,
|
|
750
|
+
originalSpiceVector,
|
|
751
|
+
timeSeconds,
|
|
752
|
+
voltageSamplesBySpiceVector,
|
|
753
|
+
voltageProbeMetadataBySpiceVector
|
|
408
754
|
}) ?? createCurrentGraphFromRequestedPlot({
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
755
|
+
normalizedSpiceVector,
|
|
756
|
+
originalSpiceVector,
|
|
757
|
+
timeSeconds,
|
|
758
|
+
currentSamplesBySpiceVector,
|
|
759
|
+
currentProbeMetadataBySpiceVector
|
|
414
760
|
});
|
|
415
761
|
if (graph) graphs.push(graph);
|
|
416
762
|
}
|
|
@@ -449,50 +795,123 @@ var eecircuitResultToSimulationGraphs = (result, spiceString) => {
|
|
|
449
795
|
};
|
|
450
796
|
var eecircuitResultToVGraphs = (result, spiceString) => {
|
|
451
797
|
return eecircuitResultToSimulationGraphs(result, spiceString).filter(
|
|
452
|
-
(graph) => graph.graphType === "voltage"
|
|
798
|
+
(graph) => graph.graphType === "voltage" && graph.analysisType === "transient"
|
|
453
799
|
);
|
|
454
800
|
};
|
|
455
801
|
|
|
456
802
|
// lib/simulation-graphs-to-circuit-json.ts
|
|
803
|
+
var simulationExperimentId = "placeholder_simulation_experiment_id";
|
|
457
804
|
var simulationGraphsToCircuitJson = (graphs, spiceString) => {
|
|
458
805
|
const tranParams = parseTranParams(spiceString);
|
|
459
806
|
return graphs.map((graph, index) => {
|
|
460
|
-
if (graph.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
807
|
+
if (graph.analysisType === "transient") {
|
|
808
|
+
if (graph.graphType === "voltage") {
|
|
809
|
+
const graphIdSource2 = graph.probeMetadata?.simulation_voltage_probe_id ?? `${index}_${graph.netName}`;
|
|
810
|
+
const graphElement2 = {
|
|
811
|
+
type: "simulation_transient_voltage_graph",
|
|
812
|
+
simulation_experiment_id: simulationExperimentId,
|
|
813
|
+
simulation_transient_voltage_graph_id: `simulation_graph_${graphIdSource2}`,
|
|
814
|
+
name: graph.netName,
|
|
815
|
+
voltage_levels: graph.voltage,
|
|
816
|
+
timestamps_ms: graph.time.map((timePoint) => timePoint * 1e3),
|
|
817
|
+
start_time_ms: (tranParams?.tstart ?? 0) * 1e3,
|
|
818
|
+
time_per_step: (tranParams?.tstep ?? 0) * 1e3,
|
|
819
|
+
end_time_ms: (tranParams?.tstop ?? 0) * 1e3,
|
|
820
|
+
source_probe_id: graph.probeMetadata?.simulation_voltage_probe_id,
|
|
821
|
+
source_probe_name: graph.probeMetadata?.name,
|
|
822
|
+
source_node_name: graph.probeMetadata?.source_node_name,
|
|
823
|
+
reference_node_name: graph.probeMetadata?.reference_node_name
|
|
824
|
+
};
|
|
825
|
+
return graphElement2;
|
|
826
|
+
}
|
|
827
|
+
const graphIdSource = graph.probeMetadata?.simulation_current_probe_id ?? `${index}_${graph.currentName}`;
|
|
828
|
+
const graphElement = {
|
|
829
|
+
type: "simulation_transient_current_graph",
|
|
830
|
+
simulation_experiment_id: simulationExperimentId,
|
|
831
|
+
simulation_transient_current_graph_id: `simulation_graph_${graphIdSource}`,
|
|
832
|
+
name: graph.currentName,
|
|
833
|
+
current_levels: graph.current,
|
|
468
834
|
timestamps_ms: graph.time.map((timePoint) => timePoint * 1e3),
|
|
469
835
|
start_time_ms: (tranParams?.tstart ?? 0) * 1e3,
|
|
470
836
|
time_per_step: (tranParams?.tstep ?? 0) * 1e3,
|
|
471
837
|
end_time_ms: (tranParams?.tstop ?? 0) * 1e3,
|
|
472
|
-
source_probe_id: graph.probeMetadata?.
|
|
838
|
+
source_probe_id: graph.probeMetadata?.simulation_current_probe_id,
|
|
473
839
|
source_probe_name: graph.probeMetadata?.name,
|
|
474
|
-
|
|
475
|
-
|
|
840
|
+
source_component_id: graph.probeMetadata?.source_component_id,
|
|
841
|
+
source_trace_id: graph.probeMetadata?.source_trace_id
|
|
842
|
+
};
|
|
843
|
+
return graphElement;
|
|
844
|
+
}
|
|
845
|
+
if (graph.analysisType === "dc_operating_point") {
|
|
846
|
+
if (graph.graphType === "voltage") {
|
|
847
|
+
const probeId3 = graph.probeMetadata?.simulation_voltage_probe_id ?? `simulation_voltage_probe_${index}_${graph.netName}`;
|
|
848
|
+
return {
|
|
849
|
+
type: "simulation_dc_operating_point_voltage",
|
|
850
|
+
simulation_dc_operating_point_voltage_id: `simulation_dc_operating_point_voltage_${probeId3}`,
|
|
851
|
+
simulation_experiment_id: simulationExperimentId,
|
|
852
|
+
simulation_voltage_probe_id: probeId3,
|
|
853
|
+
voltage: graph.voltage,
|
|
854
|
+
name: graph.netName
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
const probeId2 = graph.probeMetadata?.simulation_current_probe_id ?? `simulation_current_probe_${index}_${graph.currentName}`;
|
|
858
|
+
return {
|
|
859
|
+
type: "simulation_dc_operating_point_current",
|
|
860
|
+
simulation_dc_operating_point_current_id: `simulation_dc_operating_point_current_${probeId2}`,
|
|
861
|
+
simulation_experiment_id: simulationExperimentId,
|
|
862
|
+
simulation_current_probe_id: probeId2,
|
|
863
|
+
current: graph.current,
|
|
864
|
+
name: graph.currentName
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
if (graph.analysisType === "dc_sweep") {
|
|
868
|
+
if (graph.graphType === "voltage") {
|
|
869
|
+
const probeId3 = graph.probeMetadata?.simulation_voltage_probe_id ?? `simulation_voltage_probe_${index}_${graph.netName}`;
|
|
870
|
+
return {
|
|
871
|
+
type: "simulation_dc_sweep_voltage_graph",
|
|
872
|
+
simulation_dc_sweep_voltage_graph_id: `simulation_dc_sweep_voltage_graph_${probeId3}`,
|
|
873
|
+
simulation_experiment_id: simulationExperimentId,
|
|
874
|
+
simulation_voltage_probe_id: probeId3,
|
|
875
|
+
sweep_values: graph.sweepValues,
|
|
876
|
+
sweep_unit: graph.sweepUnit,
|
|
877
|
+
voltage_levels: graph.voltage,
|
|
878
|
+
name: graph.netName
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
const probeId2 = graph.probeMetadata?.simulation_current_probe_id ?? `simulation_current_probe_${index}_${graph.currentName}`;
|
|
882
|
+
return {
|
|
883
|
+
type: "simulation_dc_sweep_current_graph",
|
|
884
|
+
simulation_dc_sweep_current_graph_id: `simulation_dc_sweep_current_graph_${probeId2}`,
|
|
885
|
+
simulation_experiment_id: simulationExperimentId,
|
|
886
|
+
simulation_current_probe_id: probeId2,
|
|
887
|
+
sweep_values: graph.sweepValues,
|
|
888
|
+
sweep_unit: graph.sweepUnit,
|
|
889
|
+
current_levels: graph.current,
|
|
890
|
+
name: graph.currentName
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
if (graph.graphType === "voltage") {
|
|
894
|
+
const probeId2 = graph.probeMetadata?.simulation_voltage_probe_id ?? `simulation_voltage_probe_${index}_${graph.netName}`;
|
|
895
|
+
return {
|
|
896
|
+
type: "simulation_ac_sweep_voltage_graph",
|
|
897
|
+
simulation_ac_sweep_voltage_graph_id: `simulation_ac_sweep_voltage_graph_${probeId2}`,
|
|
898
|
+
simulation_experiment_id: simulationExperimentId,
|
|
899
|
+
simulation_voltage_probe_id: probeId2,
|
|
900
|
+
frequencies_hz: graph.frequenciesHz,
|
|
901
|
+
complex_voltages: graph.complexVoltages,
|
|
902
|
+
name: graph.netName
|
|
476
903
|
};
|
|
477
|
-
return graphElement2;
|
|
478
904
|
}
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
type: "
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
time_per_step: (tranParams?.tstep ?? 0) * 1e3,
|
|
489
|
-
end_time_ms: (tranParams?.tstop ?? 0) * 1e3,
|
|
490
|
-
source_probe_id: graph.probeMetadata?.simulation_current_probe_id,
|
|
491
|
-
source_probe_name: graph.probeMetadata?.name,
|
|
492
|
-
source_component_id: graph.probeMetadata?.source_component_id,
|
|
493
|
-
source_trace_id: graph.probeMetadata?.source_trace_id
|
|
905
|
+
const probeId = graph.probeMetadata?.simulation_current_probe_id ?? `simulation_current_probe_${index}_${graph.currentName}`;
|
|
906
|
+
return {
|
|
907
|
+
type: "simulation_ac_sweep_current_graph",
|
|
908
|
+
simulation_ac_sweep_current_graph_id: `simulation_ac_sweep_current_graph_${probeId}`,
|
|
909
|
+
simulation_experiment_id: simulationExperimentId,
|
|
910
|
+
simulation_current_probe_id: probeId,
|
|
911
|
+
frequencies_hz: graph.frequenciesHz,
|
|
912
|
+
complex_currents: graph.complexCurrents,
|
|
913
|
+
name: graph.currentName
|
|
494
914
|
};
|
|
495
|
-
return graphElement;
|
|
496
915
|
});
|
|
497
916
|
};
|
|
498
917
|
|
|
@@ -543,6 +962,7 @@ export {
|
|
|
543
962
|
index_default as default,
|
|
544
963
|
eecircuitResultToSimulationGraphs,
|
|
545
964
|
eecircuitResultToVGraphs,
|
|
965
|
+
parseSimulationAnalysis,
|
|
546
966
|
parseTranParams,
|
|
547
967
|
rewritePspiceCompatibilitySyntax,
|
|
548
968
|
simulationGraphsToCircuitJson
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/ngspice-spice-engine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "A tscircuit-compatible SPICE engine using ngspice.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,19 +17,21 @@
|
|
|
17
17
|
"typecheck": "tsc --noEmit"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@tscircuit/props": "^0.0.
|
|
20
|
+
"@tscircuit/props": "^0.0.592",
|
|
21
21
|
"bun-match-svg": "^0.0.14",
|
|
22
|
-
"circuit-json": "^0.0.
|
|
23
|
-
"circuit-to-svg": "^0.0.
|
|
22
|
+
"circuit-json": "^0.0.454",
|
|
23
|
+
"circuit-to-svg": "^0.0.393",
|
|
24
24
|
"format-si-unit": "^0.0.7",
|
|
25
25
|
"@biomejs/biome": "^2.2.7",
|
|
26
26
|
"@types/bun": "latest",
|
|
27
|
+
"spicets": "^0.0.4",
|
|
27
28
|
"tscircuit": "^0.0.805",
|
|
28
29
|
"tsup": "^8.5.0"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"typescript": "^5",
|
|
32
33
|
"circuit-json": "*",
|
|
33
|
-
"@tscircuit/props": "*"
|
|
34
|
+
"@tscircuit/props": "*",
|
|
35
|
+
"spicets": "*"
|
|
34
36
|
}
|
|
35
37
|
}
|