@tscircuit/ngspice-spice-engine 0.0.16 → 0.0.18
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 +60 -9
- package/dist/index.js +292 -117
- package/package.json +4 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ResultType } from '@tscircuit/eecircuit-engine';
|
|
2
1
|
import { SpiceEngine } from '@tscircuit/props';
|
|
2
|
+
import { SimulationTransientVoltageGraph, SimulationTransientCurrentGraph } from 'circuit-json';
|
|
3
3
|
|
|
4
4
|
interface TranParams {
|
|
5
5
|
tstep?: number;
|
|
@@ -12,20 +12,71 @@ declare const parseTranParams: (spiceString: string) => TranParams | null;
|
|
|
12
12
|
|
|
13
13
|
declare const rewritePspiceCompatibilitySyntax: (spiceString: string) => string;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
type ComplexNumber = {
|
|
16
|
+
real: number;
|
|
17
|
+
img: number;
|
|
18
|
+
};
|
|
19
|
+
type RealDataType = {
|
|
20
|
+
name: string;
|
|
21
|
+
type: "voltage" | "current" | "time" | "frequency" | "notype";
|
|
22
|
+
values: number[];
|
|
23
|
+
};
|
|
24
|
+
type ComplexDataType = {
|
|
25
|
+
name: string;
|
|
26
|
+
type: RealDataType["type"];
|
|
27
|
+
values: ComplexNumber[];
|
|
28
|
+
};
|
|
29
|
+
type ResultType = {
|
|
30
|
+
header: string;
|
|
31
|
+
numVariables: number;
|
|
32
|
+
variableNames: string[];
|
|
33
|
+
numPoints: number;
|
|
34
|
+
dataType: "real";
|
|
35
|
+
data: RealDataType[];
|
|
36
|
+
} | {
|
|
37
|
+
header: string;
|
|
38
|
+
numVariables: number;
|
|
39
|
+
variableNames: string[];
|
|
40
|
+
numPoints: number;
|
|
41
|
+
dataType: "complex";
|
|
42
|
+
data: ComplexDataType[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
interface VoltageProbeMetadata {
|
|
22
46
|
simulation_voltage_probe_id: string;
|
|
23
47
|
name?: string;
|
|
24
48
|
spice_vector: string;
|
|
25
49
|
source_node_name: string;
|
|
26
50
|
reference_node_name?: string;
|
|
27
51
|
}
|
|
52
|
+
interface CurrentProbeMetadata {
|
|
53
|
+
simulation_current_probe_id: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
spice_vector: string;
|
|
56
|
+
source_component_id?: string;
|
|
57
|
+
source_trace_id?: string;
|
|
58
|
+
}
|
|
59
|
+
interface VoltageGraph {
|
|
60
|
+
graphType: "voltage";
|
|
61
|
+
netName: string;
|
|
62
|
+
time: number[];
|
|
63
|
+
voltage: number[];
|
|
64
|
+
probeMetadata?: VoltageProbeMetadata;
|
|
65
|
+
}
|
|
66
|
+
interface CurrentGraph {
|
|
67
|
+
graphType: "current";
|
|
68
|
+
currentName: string;
|
|
69
|
+
time: number[];
|
|
70
|
+
current: number[];
|
|
71
|
+
probeMetadata?: CurrentProbeMetadata;
|
|
72
|
+
}
|
|
73
|
+
type SimulationGraph = VoltageGraph | CurrentGraph;
|
|
74
|
+
|
|
75
|
+
declare const eecircuitResultToSimulationGraphs: (result: ResultType, spiceString: string) => SimulationGraph[];
|
|
28
76
|
declare const eecircuitResultToVGraphs: (result: ResultType, spiceString: string) => VoltageGraph[];
|
|
77
|
+
|
|
78
|
+
declare const simulationGraphsToCircuitJson: (graphs: SimulationGraph[], spiceString: string) => Array<SimulationTransientVoltageGraph | SimulationTransientCurrentGraph>;
|
|
79
|
+
|
|
29
80
|
declare const createNgspiceSpiceEngine: () => Promise<SpiceEngine>;
|
|
30
81
|
|
|
31
|
-
export { type TranParams, createNgspiceSpiceEngine, createNgspiceSpiceEngine as default, eecircuitResultToVGraphs, parseTranParams, rewritePspiceCompatibilitySyntax };
|
|
82
|
+
export { type TranParams, createNgspiceSpiceEngine, createNgspiceSpiceEngine as default, eecircuitResultToSimulationGraphs, eecircuitResultToVGraphs, parseTranParams, rewritePspiceCompatibilitySyntax, simulationGraphsToCircuitJson };
|
package/dist/index.js
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
// lib/
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const x2 = xPoints[i];
|
|
19
|
-
const y2 = yPoints[i];
|
|
20
|
-
if (x2 === x1) {
|
|
21
|
-
return y1;
|
|
1
|
+
// lib/import-eecircuit-engine.ts
|
|
2
|
+
var EECIRCUIT_ENGINE_URL = "https://jscdn.tscircuit.com/@tscircuit/eecircuit-engine/1.7.4/+esm";
|
|
3
|
+
var modulePromise = null;
|
|
4
|
+
var importEecircuitEngine = async () => {
|
|
5
|
+
if (!modulePromise) {
|
|
6
|
+
modulePromise = fetch(EECIRCUIT_ENGINE_URL).then(async (response) => {
|
|
7
|
+
if (!response.ok) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
`Failed to load @tscircuit/eecircuit-engine from ${EECIRCUIT_ENGINE_URL}: ${response.status} ${response.statusText}`
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
const source = await response.text();
|
|
13
|
+
const moduleUrl = URL.createObjectURL(
|
|
14
|
+
new Blob([source], { type: "text/javascript" })
|
|
15
|
+
);
|
|
16
|
+
return import(moduleUrl);
|
|
17
|
+
});
|
|
22
18
|
}
|
|
23
|
-
return
|
|
19
|
+
return modulePromise;
|
|
24
20
|
};
|
|
25
21
|
|
|
26
22
|
// lib/parse-tran-params.ts
|
|
@@ -173,43 +169,84 @@ var rewritePspiceValueBooleanCarets = (spiceString) => {
|
|
|
173
169
|
};
|
|
174
170
|
var rewritePspiceCompatibilitySyntax = (spiceString) => rewritePspiceValueBooleanCarets(rewritePspiceResistorTcPairs(spiceString));
|
|
175
171
|
|
|
176
|
-
// lib/
|
|
177
|
-
var
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return instance;
|
|
182
|
-
};
|
|
183
|
-
var simulationPromise = null;
|
|
184
|
-
var getSimulation = async () => {
|
|
185
|
-
if (!simulationPromise) {
|
|
186
|
-
simulationPromise = ensureSimulation().catch((error) => {
|
|
187
|
-
simulationPromise = null;
|
|
188
|
-
throw error;
|
|
189
|
-
});
|
|
172
|
+
// lib/create-graph-from-requested-plot.ts
|
|
173
|
+
var getNetName = (rawName) => {
|
|
174
|
+
const diffMatch = rawName.match(/^v\(([^,]+),\s*([^)]+)\)$/i);
|
|
175
|
+
if (diffMatch?.[1] && diffMatch?.[2]) {
|
|
176
|
+
return `${diffMatch[1].trim()}-${diffMatch[2].trim()}`;
|
|
190
177
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const match = spiceString.match(/\.print\s+tran\s+(.*)/i);
|
|
195
|
-
if (!match?.[1]) {
|
|
196
|
-
return null;
|
|
178
|
+
const match = rawName.match(/^v\((.*)\)$/i);
|
|
179
|
+
if (!match) {
|
|
180
|
+
return rawName;
|
|
197
181
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
182
|
+
return match[1] ?? rawName;
|
|
183
|
+
};
|
|
184
|
+
var getCurrentName = (rawName) => {
|
|
185
|
+
const match = rawName.match(/^i\((.*)\)$/i);
|
|
186
|
+
if (!match) {
|
|
187
|
+
return rawName;
|
|
201
188
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
189
|
+
return match[1] ?? rawName;
|
|
190
|
+
};
|
|
191
|
+
var createVoltageGraphFromRequestedPlot = ({
|
|
192
|
+
lowerCaseToken,
|
|
193
|
+
originalToken,
|
|
194
|
+
timeValues,
|
|
195
|
+
voltageDataMap,
|
|
196
|
+
voltageProbeMetadata
|
|
197
|
+
}) => {
|
|
198
|
+
if (!lowerCaseToken.startsWith("v(")) return null;
|
|
199
|
+
const diffMatch = originalToken.match(/^v\(([^,]+),\s*([^)]+)\)$/i);
|
|
200
|
+
let voltage;
|
|
201
|
+
if (diffMatch?.[1] && diffMatch?.[2]) {
|
|
202
|
+
voltage = voltageDataMap.get(lowerCaseToken);
|
|
203
|
+
if (!voltage) {
|
|
204
|
+
const node1 = diffMatch[1].trim();
|
|
205
|
+
const node2 = diffMatch[2].trim();
|
|
206
|
+
const node1Data = voltageDataMap.get(`v(${node1.toLowerCase()})`);
|
|
207
|
+
const node2Data = voltageDataMap.get(`v(${node2.toLowerCase()})`);
|
|
208
|
+
if (node1Data && node2Data) {
|
|
209
|
+
voltage = node1Data.map((v, i) => v - (node2Data[i] ?? 0));
|
|
210
|
+
}
|
|
207
211
|
}
|
|
212
|
+
} else {
|
|
213
|
+
voltage = voltageDataMap.get(lowerCaseToken);
|
|
208
214
|
}
|
|
209
|
-
return
|
|
215
|
+
if (!voltage) return null;
|
|
216
|
+
const metadata = voltageProbeMetadata.get(lowerCaseToken);
|
|
217
|
+
const voltageGraph = {
|
|
218
|
+
graphType: "voltage",
|
|
219
|
+
netName: metadata?.name ?? getNetName(originalToken),
|
|
220
|
+
time: timeValues,
|
|
221
|
+
voltage,
|
|
222
|
+
probeMetadata: metadata
|
|
223
|
+
};
|
|
224
|
+
return voltageGraph;
|
|
210
225
|
};
|
|
226
|
+
var createCurrentGraphFromRequestedPlot = ({
|
|
227
|
+
lowerCaseToken,
|
|
228
|
+
originalToken,
|
|
229
|
+
timeValues,
|
|
230
|
+
currentDataMap,
|
|
231
|
+
currentProbeMetadata
|
|
232
|
+
}) => {
|
|
233
|
+
if (!lowerCaseToken.startsWith("i(")) return null;
|
|
234
|
+
const current = currentDataMap.get(lowerCaseToken);
|
|
235
|
+
if (!current) return null;
|
|
236
|
+
const metadata = currentProbeMetadata.get(lowerCaseToken);
|
|
237
|
+
const currentGraph = {
|
|
238
|
+
graphType: "current",
|
|
239
|
+
currentName: metadata?.name ?? getCurrentName(originalToken),
|
|
240
|
+
time: timeValues,
|
|
241
|
+
current,
|
|
242
|
+
probeMetadata: metadata
|
|
243
|
+
};
|
|
244
|
+
return currentGraph;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// lib/extract-probe-metadata.ts
|
|
211
248
|
var normalizeSpiceVector = (value) => value.toLowerCase().replace(/\s/g, "");
|
|
212
|
-
var
|
|
249
|
+
var extractVoltageProbeMetadata = (spiceString) => {
|
|
213
250
|
const metadata = /* @__PURE__ */ new Map();
|
|
214
251
|
for (const line of spiceString.split(/\r?\n/)) {
|
|
215
252
|
const match = line.match(/^\s*\*\s*tscircuit_probe\s+(.+)\s*$/);
|
|
@@ -219,30 +256,92 @@ var extractProbeMetadata = (spiceString) => {
|
|
|
219
256
|
if (typeof parsed.simulation_voltage_probe_id !== "string" || typeof parsed.spice_vector !== "string" || typeof parsed.source_node_name !== "string") {
|
|
220
257
|
continue;
|
|
221
258
|
}
|
|
222
|
-
|
|
259
|
+
const normalizedSpiceVector = normalizeSpiceVector(parsed.spice_vector);
|
|
260
|
+
const voltageProbeMetadata = {
|
|
223
261
|
simulation_voltage_probe_id: parsed.simulation_voltage_probe_id,
|
|
224
262
|
name: typeof parsed.name === "string" ? parsed.name : void 0,
|
|
225
263
|
spice_vector: parsed.spice_vector,
|
|
226
264
|
source_node_name: parsed.source_node_name,
|
|
227
265
|
reference_node_name: typeof parsed.reference_node_name === "string" ? parsed.reference_node_name : void 0
|
|
228
|
-
}
|
|
266
|
+
};
|
|
267
|
+
metadata.set(normalizedSpiceVector, voltageProbeMetadata);
|
|
229
268
|
} catch {
|
|
230
269
|
}
|
|
231
270
|
}
|
|
232
271
|
return metadata;
|
|
233
272
|
};
|
|
234
|
-
var
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
273
|
+
var extractCurrentProbeMetadata = (spiceString) => {
|
|
274
|
+
const metadata = /* @__PURE__ */ new Map();
|
|
275
|
+
for (const line of spiceString.split(/\r?\n/)) {
|
|
276
|
+
const match = line.match(/^\s*\*\s*tscircuit_current_probe\s+(.+)\s*$/);
|
|
277
|
+
if (!match?.[1]) continue;
|
|
278
|
+
try {
|
|
279
|
+
const parsed = JSON.parse(match[1]);
|
|
280
|
+
if (typeof parsed.simulation_current_probe_id !== "string" || typeof parsed.spice_vector !== "string") {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const normalizedSpiceVector = normalizeSpiceVector(parsed.spice_vector);
|
|
284
|
+
const currentProbeMetadata = {
|
|
285
|
+
simulation_current_probe_id: parsed.simulation_current_probe_id,
|
|
286
|
+
name: typeof parsed.name === "string" ? parsed.name : void 0,
|
|
287
|
+
spice_vector: parsed.spice_vector,
|
|
288
|
+
source_component_id: typeof parsed.source_component_id === "string" ? parsed.source_component_id : void 0,
|
|
289
|
+
source_trace_id: typeof parsed.source_trace_id === "string" ? parsed.source_trace_id : void 0
|
|
290
|
+
};
|
|
291
|
+
metadata.set(normalizedSpiceVector, currentProbeMetadata);
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
238
294
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
295
|
+
return metadata;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// lib/extract-requested-plots.ts
|
|
299
|
+
var extractRequestedPlots = (spiceString) => {
|
|
300
|
+
const match = spiceString.match(/\.print\s+tran\s+(.*)/i);
|
|
301
|
+
if (!match?.[1]) {
|
|
302
|
+
return null;
|
|
242
303
|
}
|
|
243
|
-
|
|
304
|
+
const tokens = match[1].match(/[VI]\s*\([^)]+\)/gi);
|
|
305
|
+
if (!tokens) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const plotMap = /* @__PURE__ */ new Map();
|
|
309
|
+
for (const token of tokens) {
|
|
310
|
+
const lowerCaseToken = token.toLowerCase().replace(/\s/g, "");
|
|
311
|
+
if (!plotMap.has(lowerCaseToken)) {
|
|
312
|
+
plotMap.set(lowerCaseToken, token);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return plotMap;
|
|
244
316
|
};
|
|
245
|
-
|
|
317
|
+
|
|
318
|
+
// lib/linear-interpolate.ts
|
|
319
|
+
var linearInterpolate = (targetX, xPoints, yPoints) => {
|
|
320
|
+
if (xPoints.length === 0) {
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
if (targetX <= xPoints[0]) {
|
|
324
|
+
return yPoints[0];
|
|
325
|
+
}
|
|
326
|
+
if (targetX >= xPoints[xPoints.length - 1]) {
|
|
327
|
+
return yPoints[yPoints.length - 1];
|
|
328
|
+
}
|
|
329
|
+
let i = 1;
|
|
330
|
+
while (i < xPoints.length && xPoints[i] < targetX) {
|
|
331
|
+
i++;
|
|
332
|
+
}
|
|
333
|
+
const x1 = xPoints[i - 1];
|
|
334
|
+
const y1 = yPoints[i - 1];
|
|
335
|
+
const x2 = xPoints[i];
|
|
336
|
+
const y2 = yPoints[i];
|
|
337
|
+
if (x2 === x1) {
|
|
338
|
+
return y1;
|
|
339
|
+
}
|
|
340
|
+
return y1 + (y2 - y1) * (targetX - x1) / (x2 - x1);
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// lib/eecircuit-result-to-simulation-graphs.ts
|
|
344
|
+
var eecircuitResultToSimulationGraphs = (result, spiceString) => {
|
|
246
345
|
if (!result?.data || result.dataType !== "real") {
|
|
247
346
|
return [];
|
|
248
347
|
}
|
|
@@ -258,46 +357,62 @@ var eecircuitResultToVGraphs = (result, spiceString) => {
|
|
|
258
357
|
for (const item of voltageDataItems) {
|
|
259
358
|
voltageDataMap.set(item.name.toLowerCase(), item.values);
|
|
260
359
|
}
|
|
360
|
+
const currentDataItems = result.data.filter(
|
|
361
|
+
(item) => item.type === "current" && Array.isArray(item.values)
|
|
362
|
+
);
|
|
363
|
+
const currentDataMap = /* @__PURE__ */ new Map();
|
|
364
|
+
for (const item of currentDataItems) {
|
|
365
|
+
currentDataMap.set(normalizeSpiceVector(item.name), item.values);
|
|
366
|
+
}
|
|
261
367
|
const requestedPlots = extractRequestedPlots(spiceString);
|
|
262
|
-
const
|
|
368
|
+
const voltageProbeMetadata = extractVoltageProbeMetadata(spiceString);
|
|
369
|
+
const currentProbeMetadata = extractCurrentProbeMetadata(spiceString);
|
|
263
370
|
if (!requestedPlots) {
|
|
264
|
-
return
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
371
|
+
return [
|
|
372
|
+
...voltageDataItems.map((item) => {
|
|
373
|
+
const metadata = voltageProbeMetadata.get(
|
|
374
|
+
normalizeSpiceVector(item.name)
|
|
375
|
+
);
|
|
376
|
+
const voltageGraph = {
|
|
377
|
+
graphType: "voltage",
|
|
378
|
+
netName: metadata?.name ?? getNetName(item.name),
|
|
379
|
+
time: timeValues,
|
|
380
|
+
voltage: item.values,
|
|
381
|
+
probeMetadata: metadata
|
|
382
|
+
};
|
|
383
|
+
return voltageGraph;
|
|
384
|
+
}),
|
|
385
|
+
...currentDataItems.map((item) => {
|
|
386
|
+
const metadata = currentProbeMetadata.get(
|
|
387
|
+
normalizeSpiceVector(item.name)
|
|
388
|
+
);
|
|
389
|
+
const currentGraph = {
|
|
390
|
+
graphType: "current",
|
|
391
|
+
currentName: metadata?.name ?? getCurrentName(item.name),
|
|
392
|
+
time: timeValues,
|
|
393
|
+
current: item.values,
|
|
394
|
+
probeMetadata: metadata
|
|
395
|
+
};
|
|
396
|
+
return currentGraph;
|
|
397
|
+
})
|
|
398
|
+
];
|
|
273
399
|
}
|
|
274
400
|
const graphs = [];
|
|
275
401
|
for (const [lowerCaseToken, originalToken] of requestedPlots.entries()) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
voltage = voltageDataMap.get(lowerCaseToken);
|
|
291
|
-
}
|
|
292
|
-
if (voltage) {
|
|
293
|
-
const metadata = probeMetadata.get(lowerCaseToken);
|
|
294
|
-
graphs.push({
|
|
295
|
-
netName: metadata?.name ?? getNetName(originalToken),
|
|
296
|
-
time: timeValues,
|
|
297
|
-
voltage,
|
|
298
|
-
probeMetadata: metadata
|
|
299
|
-
});
|
|
300
|
-
}
|
|
402
|
+
const graph = createVoltageGraphFromRequestedPlot({
|
|
403
|
+
lowerCaseToken,
|
|
404
|
+
originalToken,
|
|
405
|
+
timeValues,
|
|
406
|
+
voltageDataMap,
|
|
407
|
+
voltageProbeMetadata
|
|
408
|
+
}) ?? createCurrentGraphFromRequestedPlot({
|
|
409
|
+
lowerCaseToken,
|
|
410
|
+
originalToken,
|
|
411
|
+
timeValues,
|
|
412
|
+
currentDataMap,
|
|
413
|
+
currentProbeMetadata
|
|
414
|
+
});
|
|
415
|
+
if (graph) graphs.push(graph);
|
|
301
416
|
}
|
|
302
417
|
const tranParams = parseTranParams(spiceString);
|
|
303
418
|
if (tranParams?.tstep && tranParams.tstep > 0 && tranParams.tstop && graphs.length > 0) {
|
|
@@ -310,39 +425,94 @@ var eecircuitResultToVGraphs = (result, spiceString) => {
|
|
|
310
425
|
(_, i) => tstart + i * tstep
|
|
311
426
|
);
|
|
312
427
|
const oldTimeValues = graphs[0].time;
|
|
313
|
-
return graphs.map((graph) =>
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
428
|
+
return graphs.map((graph) => {
|
|
429
|
+
if (graph.graphType === "voltage") {
|
|
430
|
+
return {
|
|
431
|
+
...graph,
|
|
432
|
+
time: newTimeValues,
|
|
433
|
+
voltage: newTimeValues.map(
|
|
434
|
+
(t) => linearInterpolate(t, oldTimeValues, graph.voltage)
|
|
435
|
+
)
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
...graph,
|
|
440
|
+
time: newTimeValues,
|
|
441
|
+
current: newTimeValues.map(
|
|
442
|
+
(t) => linearInterpolate(t, oldTimeValues, graph.current)
|
|
443
|
+
)
|
|
444
|
+
};
|
|
445
|
+
});
|
|
320
446
|
}
|
|
321
447
|
}
|
|
322
448
|
return graphs;
|
|
323
449
|
};
|
|
324
|
-
var
|
|
450
|
+
var eecircuitResultToVGraphs = (result, spiceString) => {
|
|
451
|
+
return eecircuitResultToSimulationGraphs(result, spiceString).filter(
|
|
452
|
+
(graph) => graph.graphType === "voltage"
|
|
453
|
+
);
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
// lib/simulation-graphs-to-circuit-json.ts
|
|
457
|
+
var simulationGraphsToCircuitJson = (graphs, spiceString) => {
|
|
325
458
|
const tranParams = parseTranParams(spiceString);
|
|
326
459
|
return graphs.map((graph, index) => {
|
|
327
|
-
|
|
460
|
+
if (graph.graphType === "voltage") {
|
|
461
|
+
const graphIdSource2 = graph.probeMetadata?.simulation_voltage_probe_id ?? `${index}_${graph.netName}`;
|
|
462
|
+
const graphElement2 = {
|
|
463
|
+
type: "simulation_transient_voltage_graph",
|
|
464
|
+
simulation_experiment_id: "placeholder_simulation_experiment_id",
|
|
465
|
+
simulation_transient_voltage_graph_id: `simulation_graph_${graphIdSource2}`,
|
|
466
|
+
name: graph.netName,
|
|
467
|
+
voltage_levels: graph.voltage,
|
|
468
|
+
timestamps_ms: graph.time.map((timePoint) => timePoint * 1e3),
|
|
469
|
+
start_time_ms: (tranParams?.tstart ?? 0) * 1e3,
|
|
470
|
+
time_per_step: (tranParams?.tstep ?? 0) * 1e3,
|
|
471
|
+
end_time_ms: (tranParams?.tstop ?? 0) * 1e3,
|
|
472
|
+
source_probe_id: graph.probeMetadata?.simulation_voltage_probe_id,
|
|
473
|
+
source_probe_name: graph.probeMetadata?.name,
|
|
474
|
+
source_node_name: graph.probeMetadata?.source_node_name,
|
|
475
|
+
reference_node_name: graph.probeMetadata?.reference_node_name
|
|
476
|
+
};
|
|
477
|
+
return graphElement2;
|
|
478
|
+
}
|
|
479
|
+
const graphIdSource = graph.probeMetadata?.simulation_current_probe_id ?? `${index}_${graph.currentName}`;
|
|
328
480
|
const graphElement = {
|
|
329
|
-
type: "
|
|
481
|
+
type: "simulation_transient_current_graph",
|
|
330
482
|
simulation_experiment_id: "placeholder_simulation_experiment_id",
|
|
331
|
-
|
|
332
|
-
name: graph.
|
|
333
|
-
|
|
483
|
+
simulation_transient_current_graph_id: `simulation_graph_${graphIdSource}`,
|
|
484
|
+
name: graph.currentName,
|
|
485
|
+
current_levels: graph.current,
|
|
334
486
|
timestamps_ms: graph.time.map((timePoint) => timePoint * 1e3),
|
|
335
487
|
start_time_ms: (tranParams?.tstart ?? 0) * 1e3,
|
|
336
488
|
time_per_step: (tranParams?.tstep ?? 0) * 1e3,
|
|
337
489
|
end_time_ms: (tranParams?.tstop ?? 0) * 1e3,
|
|
338
|
-
source_probe_id: graph.probeMetadata?.
|
|
490
|
+
source_probe_id: graph.probeMetadata?.simulation_current_probe_id,
|
|
339
491
|
source_probe_name: graph.probeMetadata?.name,
|
|
340
|
-
|
|
341
|
-
|
|
492
|
+
source_component_id: graph.probeMetadata?.source_component_id,
|
|
493
|
+
source_trace_id: graph.probeMetadata?.source_trace_id
|
|
342
494
|
};
|
|
343
495
|
return graphElement;
|
|
344
496
|
});
|
|
345
497
|
};
|
|
498
|
+
|
|
499
|
+
// lib/index.ts
|
|
500
|
+
var ensureSimulation = async () => {
|
|
501
|
+
const { Simulation: SimulationCtor } = await importEecircuitEngine();
|
|
502
|
+
const instance = new SimulationCtor({ ngBehavior: "psa" });
|
|
503
|
+
await instance.start();
|
|
504
|
+
return instance;
|
|
505
|
+
};
|
|
506
|
+
var simulationPromise = null;
|
|
507
|
+
var getSimulation = async () => {
|
|
508
|
+
if (!simulationPromise) {
|
|
509
|
+
simulationPromise = ensureSimulation().catch((error) => {
|
|
510
|
+
simulationPromise = null;
|
|
511
|
+
throw error;
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
return simulationPromise;
|
|
515
|
+
};
|
|
346
516
|
var simulate = async (spiceString) => {
|
|
347
517
|
const simulation = await getSimulation();
|
|
348
518
|
const simulationSpiceString = rewritePspiceCompatibilitySyntax(spiceString);
|
|
@@ -357,9 +527,12 @@ var simulate = async (spiceString) => {
|
|
|
357
527
|
if (!result) {
|
|
358
528
|
return { simulationResultCircuitJson: [] };
|
|
359
529
|
}
|
|
360
|
-
const graphs =
|
|
530
|
+
const graphs = eecircuitResultToSimulationGraphs(
|
|
531
|
+
result,
|
|
532
|
+
simulationSpiceString
|
|
533
|
+
);
|
|
361
534
|
return {
|
|
362
|
-
simulationResultCircuitJson:
|
|
535
|
+
simulationResultCircuitJson: simulationGraphsToCircuitJson(
|
|
363
536
|
graphs,
|
|
364
537
|
simulationSpiceString
|
|
365
538
|
)
|
|
@@ -374,7 +547,9 @@ var index_default = createNgspiceSpiceEngine;
|
|
|
374
547
|
export {
|
|
375
548
|
createNgspiceSpiceEngine,
|
|
376
549
|
index_default as default,
|
|
550
|
+
eecircuitResultToSimulationGraphs,
|
|
377
551
|
eecircuitResultToVGraphs,
|
|
378
552
|
parseTranParams,
|
|
379
|
-
rewritePspiceCompatibilitySyntax
|
|
553
|
+
rewritePspiceCompatibilitySyntax,
|
|
554
|
+
simulationGraphsToCircuitJson
|
|
380
555
|
};
|
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.18",
|
|
4
4
|
"description": "A tscircuit-compatible SPICE engine using ngspice.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,11 +17,10 @@
|
|
|
17
17
|
"typecheck": "tsc --noEmit"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@tscircuit/
|
|
21
|
-
"@tscircuit/props": "^0.0.374",
|
|
20
|
+
"@tscircuit/props": "^0.0.551",
|
|
22
21
|
"bun-match-svg": "^0.0.14",
|
|
23
|
-
"circuit-json": "^0.0.
|
|
24
|
-
"circuit-to-svg": "^0.0.
|
|
22
|
+
"circuit-json": "^0.0.437",
|
|
23
|
+
"circuit-to-svg": "^0.0.357",
|
|
25
24
|
"format-si-unit": "^0.0.7",
|
|
26
25
|
"@biomejs/biome": "^2.2.7",
|
|
27
26
|
"@types/bun": "latest",
|