From 776f2cadb48454230d018d4e2a2075d182b83eaf Mon Sep 17 00:00:00 2001 From: Abse2001 Date: Wed, 5 Aug 2026 12:27:58 +0200 Subject: [PATCH 1/2] Add multidimensional sweep coordinates and measurement results --- README.md | 81 +++++++-- src/any_circuit_element.ts | 1 + src/simulation/index.ts | 1 + .../simulation_ac_sweep_current_graph.ts | 11 +- .../simulation_ac_sweep_voltage_graph.ts | 11 +- .../simulation_dc_operating_point_current.ts | 38 ++-- .../simulation_dc_operating_point_voltage.ts | 38 ++-- .../simulation_dc_sweep_current_graph.ts | 11 +- .../simulation_dc_sweep_voltage_graph.ts | 11 +- .../simulation_measurement_result.ts | 104 +++++++++++ .../simulation_parameter_sweep_coordinate.ts | 51 ++++++ .../simulation_transient_current_graph.ts | 11 +- .../simulation_transient_voltage_graph.ts | 11 +- tests/simulation_analysis_types.test.ts | 58 ++++++ tests/simulation_measurement_result.test.ts | 166 ++++++++++++++++++ 15 files changed, 537 insertions(+), 67 deletions(-) create mode 100644 src/simulation/simulation_measurement_result.ts create mode 100644 tests/simulation_measurement_result.test.ts diff --git a/README.md b/README.md index 5e754b5e..a9331fab 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ https://github.com/user-attachments/assets/2f28b7ba-689e-4d80-85b2-5bdef84b41f8 - [PcbPadTraceClearanceError](#pcbpadtraceclearanceerror) - [PcbPanel](#pcbpanel) - [PcbPanelizationPlacementError](#pcbpanelizationplacementerror) + - [PcbPin1Location](#pcbpin1location) - [PcbPlacementError](#pcbplacementerror) - [PcbPlatedHole](#pcbplatedhole) - [PcbPort](#pcbport) @@ -207,6 +208,7 @@ https://github.com/user-attachments/assets/2f28b7ba-689e-4d80-85b2-5bdef84b41f8 - [SimulationDcSweepCurrentGraph](#simulationdcsweepcurrentgraph) - [SimulationDcSweepVoltageGraph](#simulationdcsweepvoltagegraph) - [SimulationExperiment](#simulationexperiment) + - [SimulationMeasurementResult](#simulationmeasurementresult) - [SimulationOpAmp](#simulationopamp) - [SimulationOscilloscopeTrace](#simulationoscilloscopetrace) - [SimulationParameterSweep](#simulationparametersweep) @@ -501,7 +503,7 @@ interface SourceManuallyPlacedVia { type: "source_manually_placed_via" source_manually_placed_via_id: string source_group_id: string - source_net_id: string + source_net_id?: string subcircuit_id?: string source_trace_id?: string } @@ -1392,6 +1394,8 @@ interface PcbBreakoutPoint { interface PcbComponentMetadata { kicad_footprint?: KicadFootprintMetadata } + +type SupplierPin1LocationMap = Partial> ``` ### PcbComponentInvalidLayerError @@ -2206,6 +2210,24 @@ interface PcbPanelizationPlacementError extends BaseCircuitJsonError { } ``` +### PcbPin1Location + +[Source](https://github.com/tscircuit/circuit-json/blob/main/src/pcb/properties/pcb_pin1_location.ts) + +```typescript +type PcbPin1Location = + | "leftside_top" + | "leftside_bottom" + | "rightside_top" + | "rightside_bottom" + | "topside_left" + | "topside_right" + | "bottomside_left" + | "bottomside_right" + +type PcbPin1LocationRotation = 0 | 90 | 180 | 270 +``` + ### PcbPlacementError [Source](https://github.com/tscircuit/circuit-json/blob/main/src/pcb/pcb_placement_error.ts) @@ -2831,6 +2853,8 @@ interface PcbVia { to_layer?: LayerRef layers: LayerRef[] pcb_trace_id?: string + source_trace_id?: string + source_net_id?: string net_is_assignable?: boolean net_assigned?: boolean is_tented?: boolean @@ -3446,11 +3470,11 @@ interface SchematicVoltageProbe { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_ac_sweep_current_graph.ts) ```typescript -interface SimulationAcSweepCurrentGraph { +interface SimulationAcSweepCurrentGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_ac_sweep_current_graph" simulation_ac_sweep_current_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_current_probe_id: string frequencies_hz: number[] complex_currents: SimulationComplexSample[] @@ -3464,11 +3488,11 @@ interface SimulationAcSweepCurrentGraph { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_ac_sweep_voltage_graph.ts) ```typescript -interface SimulationAcSweepVoltageGraph { +interface SimulationAcSweepVoltageGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_ac_sweep_voltage_graph" simulation_ac_sweep_voltage_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_voltage_probe_id: string frequencies_hz: number[] complex_voltages: SimulationComplexSample[] @@ -3586,11 +3610,11 @@ interface SimulationAcCurrentSource { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_operating_point_current.ts) ```typescript -interface SimulationDcOperatingPointCurrent { +interface SimulationDcOperatingPointCurrent + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_operating_point_current" simulation_dc_operating_point_current_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_current_probe_id: string current: number name?: string @@ -3603,11 +3627,11 @@ interface SimulationDcOperatingPointCurrent { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_operating_point_voltage.ts) ```typescript -interface SimulationDcOperatingPointVoltage { +interface SimulationDcOperatingPointVoltage + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_operating_point_voltage" simulation_dc_operating_point_voltage_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_voltage_probe_id: string voltage: number name?: string @@ -3620,11 +3644,11 @@ interface SimulationDcOperatingPointVoltage { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_sweep_current_graph.ts) ```typescript -interface SimulationDcSweepCurrentGraph { +interface SimulationDcSweepCurrentGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_sweep_current_graph" simulation_dc_sweep_current_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_current_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -3639,11 +3663,11 @@ interface SimulationDcSweepCurrentGraph { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_sweep_voltage_graph.ts) ```typescript -interface SimulationDcSweepVoltageGraph { +interface SimulationDcSweepVoltageGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_sweep_voltage_graph" simulation_dc_sweep_voltage_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_voltage_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -3688,6 +3712,22 @@ interface SpiceSimulationOptions { } ``` +### SimulationMeasurementResult + +[Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_measurement_result.ts) + +```typescript +interface SimulationMeasurementResult { + type: "simulation_measurement_result" + simulation_measurement_result_id: string + simulation_experiment_id: string + name: string + measurement_values: number[] + measurement_unit: string + simulation_parameter_sweep_coordinate_sets?: SimulationParameterSweepCoordinate[][] +} +``` + ### SimulationOpAmp [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_op_amp.ts) @@ -3788,6 +3828,13 @@ interface SimulationParameterSweepCoordinate { parameter_value: number parameter_unit: SimulationParameterUnit } + +interface SimulationParameterSweepResultCoordinates { + /** Coordinate for a one-dimensional parameter sweep. */ + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + /** Ordered coordinates for a multidimensional parameter sweep. */ + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] +} ``` ### SimulationSpiceSubcircuit @@ -3834,11 +3881,11 @@ interface SimulationSwitch { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_transient_current_graph.ts) ```typescript -interface SimulationTransientCurrentGraph { +interface SimulationTransientCurrentGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_transient_current_graph" simulation_transient_current_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate timestamps_ms?: number[] current_levels: number[] source_component_id?: string @@ -3856,11 +3903,11 @@ interface SimulationTransientCurrentGraph { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_transient_voltage_graph.ts) ```typescript -interface SimulationTransientVoltageGraph { +interface SimulationTransientVoltageGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_transient_voltage_graph" simulation_transient_voltage_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate timestamps_ms?: number[] voltage_levels: number[] source_component_id?: string diff --git a/src/any_circuit_element.ts b/src/any_circuit_element.ts index 5697e7fa..4fcb2be8 100644 --- a/src/any_circuit_element.ts +++ b/src/any_circuit_element.ts @@ -161,6 +161,7 @@ export const any_circuit_element = z.union([ sim.simulation_dc_sweep_current_graph, sim.simulation_ac_sweep_voltage_graph, sim.simulation_ac_sweep_current_graph, + sim.simulation_measurement_result, sim.simulation_parameter_sweep, sim.simulation_switch, sim.simulation_voltage_probe, diff --git a/src/simulation/index.ts b/src/simulation/index.ts index 017b9582..1fa81b32 100644 --- a/src/simulation/index.ts +++ b/src/simulation/index.ts @@ -21,3 +21,4 @@ export * from "./simulation_units" export * from "./simulation_op_amp" export * from "./simulation_spice_subcircuit" export * from "./simulation_oscilloscope_trace" +export * from "./simulation_measurement_result" diff --git a/src/simulation/simulation_ac_sweep_current_graph.ts b/src/simulation/simulation_ac_sweep_current_graph.ts index 398c11a1..457f98b4 100644 --- a/src/simulation/simulation_ac_sweep_current_graph.ts +++ b/src/simulation/simulation_ac_sweep_current_graph.ts @@ -7,14 +7,16 @@ import { } from "./simulation_complex_sample" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationAcSweepCurrentGraph { +export interface SimulationAcSweepCurrentGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_ac_sweep_current_graph" simulation_ac_sweep_current_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_current_probe_id: string frequencies_hz: number[] complex_currents: SimulationComplexSample[] @@ -31,12 +33,15 @@ export const simulation_ac_sweep_current_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), simulation_current_probe_id: z.string(), frequencies_hz: z.array(z.number()), complex_currents: z.array(simulation_complex_sample), name: z.string().optional(), color: z.string().optional(), }) + .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.frequencies_hz.length === graph.complex_currents.length, { diff --git a/src/simulation/simulation_ac_sweep_voltage_graph.ts b/src/simulation/simulation_ac_sweep_voltage_graph.ts index 783987d6..8d626215 100644 --- a/src/simulation/simulation_ac_sweep_voltage_graph.ts +++ b/src/simulation/simulation_ac_sweep_voltage_graph.ts @@ -7,14 +7,16 @@ import { } from "./simulation_complex_sample" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationAcSweepVoltageGraph { +export interface SimulationAcSweepVoltageGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_ac_sweep_voltage_graph" simulation_ac_sweep_voltage_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_voltage_probe_id: string frequencies_hz: number[] complex_voltages: SimulationComplexSample[] @@ -31,12 +33,15 @@ export const simulation_ac_sweep_voltage_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), simulation_voltage_probe_id: z.string(), frequencies_hz: z.array(z.number()), complex_voltages: z.array(simulation_complex_sample), name: z.string().optional(), color: z.string().optional(), }) + .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.frequencies_hz.length === graph.complex_voltages.length, { diff --git a/src/simulation/simulation_dc_operating_point_current.ts b/src/simulation/simulation_dc_operating_point_current.ts index 329cebc0..ef2e40ec 100644 --- a/src/simulation/simulation_dc_operating_point_current.ts +++ b/src/simulation/simulation_dc_operating_point_current.ts @@ -3,33 +3,39 @@ import { getZodPrefixedIdWithDefault } from "src/common" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcOperatingPointCurrent { +export interface SimulationDcOperatingPointCurrent + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_operating_point_current" simulation_dc_operating_point_current_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_current_probe_id: string current: number name?: string color?: string } -export const simulation_dc_operating_point_current = z.object({ - type: z.literal("simulation_dc_operating_point_current"), - simulation_dc_operating_point_current_id: getZodPrefixedIdWithDefault( - "simulation_dc_operating_point_current", - ), - simulation_experiment_id: z.string(), - simulation_parameter_sweep_coordinate: - simulation_parameter_sweep_coordinate.optional(), - simulation_current_probe_id: z.string(), - current: z.number(), - name: z.string().optional(), - color: z.string().optional(), -}) +export const simulation_dc_operating_point_current = z + .object({ + type: z.literal("simulation_dc_operating_point_current"), + simulation_dc_operating_point_current_id: getZodPrefixedIdWithDefault( + "simulation_dc_operating_point_current", + ), + simulation_experiment_id: z.string(), + simulation_parameter_sweep_coordinate: + simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), + simulation_current_probe_id: z.string(), + current: z.number(), + name: z.string().optional(), + color: z.string().optional(), + }) + .superRefine(validateSimulationParameterSweepResultCoordinates) export type SimulationDcOperatingPointCurrentInput = z.input< typeof simulation_dc_operating_point_current diff --git a/src/simulation/simulation_dc_operating_point_voltage.ts b/src/simulation/simulation_dc_operating_point_voltage.ts index b49dfa52..e2f3bdd4 100644 --- a/src/simulation/simulation_dc_operating_point_voltage.ts +++ b/src/simulation/simulation_dc_operating_point_voltage.ts @@ -3,33 +3,39 @@ import { getZodPrefixedIdWithDefault } from "src/common" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcOperatingPointVoltage { +export interface SimulationDcOperatingPointVoltage + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_operating_point_voltage" simulation_dc_operating_point_voltage_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_voltage_probe_id: string voltage: number name?: string color?: string } -export const simulation_dc_operating_point_voltage = z.object({ - type: z.literal("simulation_dc_operating_point_voltage"), - simulation_dc_operating_point_voltage_id: getZodPrefixedIdWithDefault( - "simulation_dc_operating_point_voltage", - ), - simulation_experiment_id: z.string(), - simulation_parameter_sweep_coordinate: - simulation_parameter_sweep_coordinate.optional(), - simulation_voltage_probe_id: z.string(), - voltage: z.number(), - name: z.string().optional(), - color: z.string().optional(), -}) +export const simulation_dc_operating_point_voltage = z + .object({ + type: z.literal("simulation_dc_operating_point_voltage"), + simulation_dc_operating_point_voltage_id: getZodPrefixedIdWithDefault( + "simulation_dc_operating_point_voltage", + ), + simulation_experiment_id: z.string(), + simulation_parameter_sweep_coordinate: + simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), + simulation_voltage_probe_id: z.string(), + voltage: z.number(), + name: z.string().optional(), + color: z.string().optional(), + }) + .superRefine(validateSimulationParameterSweepResultCoordinates) export type SimulationDcOperatingPointVoltageInput = z.input< typeof simulation_dc_operating_point_voltage diff --git a/src/simulation/simulation_dc_sweep_current_graph.ts b/src/simulation/simulation_dc_sweep_current_graph.ts index 9f14f897..7aede443 100644 --- a/src/simulation/simulation_dc_sweep_current_graph.ts +++ b/src/simulation/simulation_dc_sweep_current_graph.ts @@ -7,14 +7,16 @@ import { } from "./simulation_units" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcSweepCurrentGraph { +export interface SimulationDcSweepCurrentGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_sweep_current_graph" simulation_dc_sweep_current_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_current_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -32,6 +34,8 @@ export const simulation_dc_sweep_current_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), simulation_current_probe_id: z.string(), sweep_values: z.array(z.number()), sweep_unit: simulation_dc_sweep_unit, @@ -39,6 +43,7 @@ export const simulation_dc_sweep_current_graph = z name: z.string().optional(), color: z.string().optional(), }) + .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.sweep_values.length === graph.current_levels.length, { diff --git a/src/simulation/simulation_dc_sweep_voltage_graph.ts b/src/simulation/simulation_dc_sweep_voltage_graph.ts index 2ab80d38..a9225757 100644 --- a/src/simulation/simulation_dc_sweep_voltage_graph.ts +++ b/src/simulation/simulation_dc_sweep_voltage_graph.ts @@ -7,14 +7,16 @@ import { } from "./simulation_units" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcSweepVoltageGraph { +export interface SimulationDcSweepVoltageGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_dc_sweep_voltage_graph" simulation_dc_sweep_voltage_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate simulation_voltage_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -32,6 +34,8 @@ export const simulation_dc_sweep_voltage_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), simulation_voltage_probe_id: z.string(), sweep_values: z.array(z.number()), sweep_unit: simulation_dc_sweep_unit, @@ -39,6 +43,7 @@ export const simulation_dc_sweep_voltage_graph = z name: z.string().optional(), color: z.string().optional(), }) + .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.sweep_values.length === graph.voltage_levels.length, { diff --git a/src/simulation/simulation_measurement_result.ts b/src/simulation/simulation_measurement_result.ts new file mode 100644 index 00000000..df102e60 --- /dev/null +++ b/src/simulation/simulation_measurement_result.ts @@ -0,0 +1,104 @@ +import { z } from "zod" +import { getZodPrefixedIdWithDefault } from "src/common" +import { expectTypesMatch } from "src/utils/expect-types-match" +import { + simulation_parameter_sweep_coordinate_set, + type SimulationParameterSweepCoordinate, +} from "./simulation_parameter_sweep_coordinate" + +export interface SimulationMeasurementResult { + type: "simulation_measurement_result" + simulation_measurement_result_id: string + simulation_experiment_id: string + name: string + measurement_values: number[] + measurement_unit: string + simulation_parameter_sweep_coordinate_sets?: SimulationParameterSweepCoordinate[][] +} + +const finiteMeasurementValue = z.number().refine(Number.isFinite, { + message: "Measurement values must be finite", +}) + +export const simulation_measurement_result = z + .object({ + type: z.literal("simulation_measurement_result"), + simulation_measurement_result_id: getZodPrefixedIdWithDefault( + "simulation_measurement_result", + ), + simulation_experiment_id: z.string(), + name: z.string().min(1), + measurement_values: z.array(finiteMeasurementValue).min(1), + measurement_unit: z.string().min(1), + simulation_parameter_sweep_coordinate_sets: z + .array(simulation_parameter_sweep_coordinate_set) + .min(1) + .optional(), + }) + .superRefine((result, context) => { + const coordinateSets = result.simulation_parameter_sweep_coordinate_sets + if (!coordinateSets) { + if (result.measurement_values.length !== 1) { + context.addIssue({ + code: z.ZodIssueCode.custom, + path: ["measurement_values"], + message: + "A measurement result without parameter sweeps must contain exactly one value", + }) + } + return + } + + if (coordinateSets.length !== result.measurement_values.length) { + context.addIssue({ + code: z.ZodIssueCode.custom, + path: ["simulation_parameter_sweep_coordinate_sets"], + message: + "measurement_values and simulation_parameter_sweep_coordinate_sets must have the same length", + }) + } + + const expectedParameterSweepIds = coordinateSets[0]?.map( + (coordinate) => coordinate.simulation_parameter_sweep_id, + ) + if (!expectedParameterSweepIds) return + + for (const [ + coordinateSetIndex, + coordinateSet, + ] of coordinateSets.entries()) { + const parameterSweepIds = coordinateSet.map( + (coordinate) => coordinate.simulation_parameter_sweep_id, + ) + const hasExpectedSweepOrder = + parameterSweepIds.length === expectedParameterSweepIds.length && + parameterSweepIds.every( + (parameterSweepId, sweepIndex) => + parameterSweepId === expectedParameterSweepIds[sweepIndex], + ) + if (!hasExpectedSweepOrder) { + context.addIssue({ + code: z.ZodIssueCode.custom, + path: [ + "simulation_parameter_sweep_coordinate_sets", + coordinateSetIndex, + ], + message: + "Every coordinate set must contain the same parameter sweeps in the same order", + }) + } + } + }) + .describe("Stores scalar measurements for a simulation experiment") + +export type SimulationMeasurementResultInput = z.input< + typeof simulation_measurement_result +> +type InferredSimulationMeasurementResult = z.infer< + typeof simulation_measurement_result +> + +expectTypesMatch< + SimulationMeasurementResult, + InferredSimulationMeasurementResult +>(true) diff --git a/src/simulation/simulation_parameter_sweep_coordinate.ts b/src/simulation/simulation_parameter_sweep_coordinate.ts index ec470584..5b0f9657 100644 --- a/src/simulation/simulation_parameter_sweep_coordinate.ts +++ b/src/simulation/simulation_parameter_sweep_coordinate.ts @@ -12,6 +12,13 @@ export interface SimulationParameterSweepCoordinate { parameter_unit: SimulationParameterUnit } +export interface SimulationParameterSweepResultCoordinates { + /** Coordinate for a one-dimensional parameter sweep. */ + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + /** Ordered coordinates for a multidimensional parameter sweep. */ + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] +} + export const simulation_parameter_sweep_coordinate = z.object({ simulation_parameter_sweep_id: z.string(), sweep_index: z.number().int().nonnegative(), @@ -19,6 +26,50 @@ export const simulation_parameter_sweep_coordinate = z.object({ parameter_unit: simulation_parameter_unit, }) +const rejectDuplicateParameterSweepIds = ( + coordinates: SimulationParameterSweepCoordinate[], + context: z.RefinementCtx, +) => { + const seenParameterSweepIds = new Set() + for (const [coordinateIndex, coordinate] of coordinates.entries()) { + if (seenParameterSweepIds.has(coordinate.simulation_parameter_sweep_id)) { + context.addIssue({ + code: z.ZodIssueCode.custom, + path: [coordinateIndex, "simulation_parameter_sweep_id"], + message: "Parameter sweep coordinate IDs must be unique", + }) + } + seenParameterSweepIds.add(coordinate.simulation_parameter_sweep_id) + } +} + +export const simulation_parameter_sweep_coordinate_set = z + .array(simulation_parameter_sweep_coordinate) + .min(1) + .superRefine(rejectDuplicateParameterSweepIds) + +export const simulation_parameter_sweep_coordinates = z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .superRefine(rejectDuplicateParameterSweepIds) + +export const validateSimulationParameterSweepResultCoordinates = ( + result: SimulationParameterSweepResultCoordinates, + context: z.RefinementCtx, +) => { + if ( + result.simulation_parameter_sweep_coordinate && + result.simulation_parameter_sweep_coordinates + ) { + context.addIssue({ + code: z.ZodIssueCode.custom, + path: ["simulation_parameter_sweep_coordinates"], + message: + "Use the singular coordinate for one-dimensional sweeps or the plural coordinates for multidimensional sweeps, not both", + }) + } +} + expectTypesMatch< SimulationParameterSweepCoordinate, z.infer diff --git a/src/simulation/simulation_transient_current_graph.ts b/src/simulation/simulation_transient_current_graph.ts index cd423516..da8af36f 100644 --- a/src/simulation/simulation_transient_current_graph.ts +++ b/src/simulation/simulation_transient_current_graph.ts @@ -4,14 +4,16 @@ import { duration_ms, ms } from "src/units" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationTransientCurrentGraph { +export interface SimulationTransientCurrentGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_transient_current_graph" simulation_transient_current_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate timestamps_ms?: number[] current_levels: number[] source_component_id?: string @@ -32,6 +34,8 @@ export const simulation_transient_current_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), timestamps_ms: z.array(z.number()).optional(), current_levels: z.array(z.number()), source_component_id: z.string().optional(), @@ -42,6 +46,7 @@ export const simulation_transient_current_graph = z name: z.string().optional(), color: z.string().optional(), }) + .superRefine(validateSimulationParameterSweepResultCoordinates) .describe("Stores current measurements over time for a simulation") export type SimulationTransientCurrentGraphInput = z.input< diff --git a/src/simulation/simulation_transient_voltage_graph.ts b/src/simulation/simulation_transient_voltage_graph.ts index 8a732bb9..60b692cc 100644 --- a/src/simulation/simulation_transient_voltage_graph.ts +++ b/src/simulation/simulation_transient_voltage_graph.ts @@ -4,14 +4,16 @@ import { duration_ms, ms } from "src/units" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - type SimulationParameterSweepCoordinate, + simulation_parameter_sweep_coordinates, + type SimulationParameterSweepResultCoordinates, + validateSimulationParameterSweepResultCoordinates, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationTransientVoltageGraph { +export interface SimulationTransientVoltageGraph + extends SimulationParameterSweepResultCoordinates { type: "simulation_transient_voltage_graph" simulation_transient_voltage_graph_id: string simulation_experiment_id: string - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate timestamps_ms?: number[] voltage_levels: number[] source_component_id?: string @@ -32,6 +34,8 @@ export const simulation_transient_voltage_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: + simulation_parameter_sweep_coordinates.optional(), timestamps_ms: z.array(z.number()).optional(), voltage_levels: z.array(z.number()), source_component_id: z.string().optional(), @@ -42,6 +46,7 @@ export const simulation_transient_voltage_graph = z name: z.string().optional(), color: z.string().optional(), }) + .superRefine(validateSimulationParameterSweepResultCoordinates) .describe("Stores voltage measurements over time for a simulation") export type SimulationTransientVoltageGraphInput = z.input< diff --git a/tests/simulation_analysis_types.test.ts b/tests/simulation_analysis_types.test.ts index c3ff2fad..347000bb 100644 --- a/tests/simulation_analysis_types.test.ts +++ b/tests/simulation_analysis_types.test.ts @@ -100,3 +100,61 @@ test("parses analysis-specific simulation results", () => { }) expect(acSweep.complex_voltages[1]).toEqual({ re: 0.5, im: -0.5 }) }) + +test("parses multidimensional sweep coordinates on analysis results", () => { + const result = simulation_dc_operating_point_voltage.parse({ + type: "simulation_dc_operating_point_voltage", + simulation_experiment_id: "simulation_experiment_0", + simulation_voltage_probe_id: "simulation_voltage_probe_0", + voltage: 3.3, + simulation_parameter_sweep_coordinates: [ + { + simulation_parameter_sweep_id: "simulation_parameter_sweep_voltage", + sweep_index: 1, + parameter_value: 3.6, + parameter_unit: "V", + }, + { + simulation_parameter_sweep_id: "simulation_parameter_sweep_current", + sweep_index: 4, + parameter_value: 1, + parameter_unit: "A", + }, + ], + }) + + expect(result.simulation_parameter_sweep_coordinates).toHaveLength(2) +}) + +test("rejects singular and multidimensional sweep coordinates together", () => { + expect(() => + simulation_dc_operating_point_voltage.parse({ + type: "simulation_dc_operating_point_voltage", + simulation_experiment_id: "simulation_experiment_0", + simulation_voltage_probe_id: "simulation_voltage_probe_0", + voltage: 3.3, + simulation_parameter_sweep_coordinate: { + simulation_parameter_sweep_id: "simulation_parameter_sweep_voltage", + sweep_index: 1, + parameter_value: 3.6, + parameter_unit: "V", + }, + simulation_parameter_sweep_coordinates: [ + { + simulation_parameter_sweep_id: "simulation_parameter_sweep_voltage", + sweep_index: 1, + parameter_value: 3.6, + parameter_unit: "V", + }, + { + simulation_parameter_sweep_id: "simulation_parameter_sweep_current", + sweep_index: 4, + parameter_value: 1, + parameter_unit: "A", + }, + ], + }), + ).toThrow( + "Use the singular coordinate for one-dimensional sweeps or the plural coordinates for multidimensional sweeps, not both", + ) +}) diff --git a/tests/simulation_measurement_result.test.ts b/tests/simulation_measurement_result.test.ts new file mode 100644 index 00000000..a875fc40 --- /dev/null +++ b/tests/simulation_measurement_result.test.ts @@ -0,0 +1,166 @@ +import { expect, test } from "bun:test" +import { any_circuit_element, simulation_measurement_result } from "../src" + +const inputVoltageCoordinate = { + simulation_parameter_sweep_id: "simulation_parameter_sweep_input_voltage", + sweep_index: 0, + parameter_value: 2.5, + parameter_unit: "V" as const, +} + +const loadCurrentCoordinate = { + simulation_parameter_sweep_id: "simulation_parameter_sweep_load_current", + sweep_index: 0, + parameter_value: 0, + parameter_unit: "A" as const, +} + +test("parses a multidimensional simulation measurement result", () => { + const result = simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_load_regulation", + name: "output-voltage-regulation", + measurement_values: [-0.08, -0.07], + measurement_unit: "%", + simulation_parameter_sweep_coordinate_sets: [ + [inputVoltageCoordinate, loadCurrentCoordinate], + [ + inputVoltageCoordinate, + { + ...loadCurrentCoordinate, + sweep_index: 1, + parameter_value: 0.25, + }, + ], + ], + }) + + expect(result.measurement_values).toEqual([-0.08, -0.07]) + expect(result.simulation_parameter_sweep_coordinate_sets?.[1]?.[1]).toEqual({ + ...loadCurrentCoordinate, + sweep_index: 1, + parameter_value: 0.25, + }) + expect(any_circuit_element.parse(result).type).toBe( + "simulation_measurement_result", + ) +}) + +test("parses one measurement value without parameter sweeps", () => { + const result = simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_bias", + name: "output-voltage", + measurement_values: [3.3], + measurement_unit: "V", + }) + + expect(result.measurement_values).toEqual([3.3]) + expect(result.simulation_parameter_sweep_coordinate_sets).toBeUndefined() +}) + +test("parses a one-dimensional swept measurement result", () => { + const result = simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_load_regulation", + name: "output-voltage", + measurement_values: [3.301, 3.299], + measurement_unit: "V", + simulation_parameter_sweep_coordinate_sets: [ + [loadCurrentCoordinate], + [ + { + ...loadCurrentCoordinate, + sweep_index: 1, + parameter_value: 0.25, + }, + ], + ], + }) + + expect(result.simulation_parameter_sweep_coordinate_sets).toHaveLength(2) + expect(result.simulation_parameter_sweep_coordinate_sets?.[0]).toHaveLength(1) +}) + +test("rejects measurement values without matching coordinate sets", () => { + expect(() => + simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_load_regulation", + name: "output-voltage-regulation", + measurement_values: [-0.08, -0.07], + measurement_unit: "%", + simulation_parameter_sweep_coordinate_sets: [ + [inputVoltageCoordinate, loadCurrentCoordinate], + ], + }), + ).toThrow( + "measurement_values and simulation_parameter_sweep_coordinate_sets must have the same length", + ) +}) + +test("rejects multiple values when there are no parameter sweeps", () => { + expect(() => + simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_bias", + name: "output-voltage", + measurement_values: [3.3, 3.29], + measurement_unit: "V", + }), + ).toThrow( + "A measurement result without parameter sweeps must contain exactly one value", + ) +}) + +test("rejects coordinate sets with inconsistent sweep order", () => { + expect(() => + simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_load_regulation", + name: "output-voltage-regulation", + measurement_values: [-0.08, -0.07], + measurement_unit: "%", + simulation_parameter_sweep_coordinate_sets: [ + [inputVoltageCoordinate, loadCurrentCoordinate], + [loadCurrentCoordinate, inputVoltageCoordinate], + ], + }), + ).toThrow( + "Every coordinate set must contain the same parameter sweeps in the same order", + ) +}) + +test("rejects duplicate sweep IDs in one coordinate set", () => { + expect(() => + simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_load_regulation", + name: "output-voltage-regulation", + measurement_values: [-0.08], + measurement_unit: "%", + simulation_parameter_sweep_coordinate_sets: [ + [ + inputVoltageCoordinate, + { + ...inputVoltageCoordinate, + parameter_value: 3.6, + sweep_index: 1, + }, + ], + ], + }), + ).toThrow("Parameter sweep coordinate IDs must be unique") +}) + +test("rejects non-finite measurement values", () => { + expect(() => + simulation_measurement_result.parse({ + type: "simulation_measurement_result", + simulation_experiment_id: "simulation_experiment_bias", + name: "output-voltage", + measurement_values: [Number.NaN], + measurement_unit: "V", + }), + ).toThrow() +}) From afd77834fc07e09522a68b9b2b5b986f92d77ba0 Mon Sep 17 00:00:00 2001 From: Abse2001 Date: Wed, 5 Aug 2026 18:10:56 +0200 Subject: [PATCH 2/2] Flatten simulation result schemas --- README.md | 47 ++++++++--------- .../simulation_ac_sweep_current_graph.ts | 16 +++--- .../simulation_ac_sweep_voltage_graph.ts | 16 +++--- .../simulation_dc_operating_point_current.ts | 43 ++++++++-------- .../simulation_dc_operating_point_voltage.ts | 43 ++++++++-------- .../simulation_dc_sweep_current_graph.ts | 16 +++--- .../simulation_dc_sweep_voltage_graph.ts | 16 +++--- .../simulation_measurement_result.ts | 41 ++------------- .../simulation_parameter_sweep_coordinate.ts | 51 ------------------- .../simulation_transient_current_graph.ts | 16 +++--- .../simulation_transient_voltage_graph.ts | 16 +++--- tests/simulation_analysis_types.test.ts | 33 ------------ tests/simulation_measurement_result.test.ts | 40 --------------- 13 files changed, 117 insertions(+), 277 deletions(-) diff --git a/README.md b/README.md index a9331fab..fb8ea49e 100644 --- a/README.md +++ b/README.md @@ -3470,11 +3470,12 @@ interface SchematicVoltageProbe { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_ac_sweep_current_graph.ts) ```typescript -interface SimulationAcSweepCurrentGraph - extends SimulationParameterSweepResultCoordinates { +interface SimulationAcSweepCurrentGraph { type: "simulation_ac_sweep_current_graph" simulation_ac_sweep_current_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_current_probe_id: string frequencies_hz: number[] complex_currents: SimulationComplexSample[] @@ -3488,11 +3489,12 @@ interface SimulationAcSweepCurrentGraph [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_ac_sweep_voltage_graph.ts) ```typescript -interface SimulationAcSweepVoltageGraph - extends SimulationParameterSweepResultCoordinates { +interface SimulationAcSweepVoltageGraph { type: "simulation_ac_sweep_voltage_graph" simulation_ac_sweep_voltage_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_voltage_probe_id: string frequencies_hz: number[] complex_voltages: SimulationComplexSample[] @@ -3610,11 +3612,12 @@ interface SimulationAcCurrentSource { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_operating_point_current.ts) ```typescript -interface SimulationDcOperatingPointCurrent - extends SimulationParameterSweepResultCoordinates { +interface SimulationDcOperatingPointCurrent { type: "simulation_dc_operating_point_current" simulation_dc_operating_point_current_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_current_probe_id: string current: number name?: string @@ -3627,11 +3630,12 @@ interface SimulationDcOperatingPointCurrent [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_operating_point_voltage.ts) ```typescript -interface SimulationDcOperatingPointVoltage - extends SimulationParameterSweepResultCoordinates { +interface SimulationDcOperatingPointVoltage { type: "simulation_dc_operating_point_voltage" simulation_dc_operating_point_voltage_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_voltage_probe_id: string voltage: number name?: string @@ -3644,11 +3648,12 @@ interface SimulationDcOperatingPointVoltage [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_sweep_current_graph.ts) ```typescript -interface SimulationDcSweepCurrentGraph - extends SimulationParameterSweepResultCoordinates { +interface SimulationDcSweepCurrentGraph { type: "simulation_dc_sweep_current_graph" simulation_dc_sweep_current_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_current_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -3663,11 +3668,12 @@ interface SimulationDcSweepCurrentGraph [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_dc_sweep_voltage_graph.ts) ```typescript -interface SimulationDcSweepVoltageGraph - extends SimulationParameterSweepResultCoordinates { +interface SimulationDcSweepVoltageGraph { type: "simulation_dc_sweep_voltage_graph" simulation_dc_sweep_voltage_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_voltage_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -3828,13 +3834,6 @@ interface SimulationParameterSweepCoordinate { parameter_value: number parameter_unit: SimulationParameterUnit } - -interface SimulationParameterSweepResultCoordinates { - /** Coordinate for a one-dimensional parameter sweep. */ - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate - /** Ordered coordinates for a multidimensional parameter sweep. */ - simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] -} ``` ### SimulationSpiceSubcircuit @@ -3881,11 +3880,12 @@ interface SimulationSwitch { [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_transient_current_graph.ts) ```typescript -interface SimulationTransientCurrentGraph - extends SimulationParameterSweepResultCoordinates { +interface SimulationTransientCurrentGraph { type: "simulation_transient_current_graph" simulation_transient_current_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] timestamps_ms?: number[] current_levels: number[] source_component_id?: string @@ -3903,11 +3903,12 @@ interface SimulationTransientCurrentGraph [Source](https://github.com/tscircuit/circuit-json/blob/main/src/simulation/simulation_transient_voltage_graph.ts) ```typescript -interface SimulationTransientVoltageGraph - extends SimulationParameterSweepResultCoordinates { +interface SimulationTransientVoltageGraph { type: "simulation_transient_voltage_graph" simulation_transient_voltage_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] timestamps_ms?: number[] voltage_levels: number[] source_component_id?: string diff --git a/src/simulation/simulation_ac_sweep_current_graph.ts b/src/simulation/simulation_ac_sweep_current_graph.ts index 457f98b4..b3d9cf1e 100644 --- a/src/simulation/simulation_ac_sweep_current_graph.ts +++ b/src/simulation/simulation_ac_sweep_current_graph.ts @@ -7,16 +7,15 @@ import { } from "./simulation_complex_sample" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationAcSweepCurrentGraph - extends SimulationParameterSweepResultCoordinates { +export interface SimulationAcSweepCurrentGraph { type: "simulation_ac_sweep_current_graph" simulation_ac_sweep_current_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_current_probe_id: string frequencies_hz: number[] complex_currents: SimulationComplexSample[] @@ -33,15 +32,16 @@ export const simulation_ac_sweep_current_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), simulation_current_probe_id: z.string(), frequencies_hz: z.array(z.number()), complex_currents: z.array(simulation_complex_sample), name: z.string().optional(), color: z.string().optional(), }) - .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.frequencies_hz.length === graph.complex_currents.length, { diff --git a/src/simulation/simulation_ac_sweep_voltage_graph.ts b/src/simulation/simulation_ac_sweep_voltage_graph.ts index 8d626215..5086e688 100644 --- a/src/simulation/simulation_ac_sweep_voltage_graph.ts +++ b/src/simulation/simulation_ac_sweep_voltage_graph.ts @@ -7,16 +7,15 @@ import { } from "./simulation_complex_sample" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationAcSweepVoltageGraph - extends SimulationParameterSweepResultCoordinates { +export interface SimulationAcSweepVoltageGraph { type: "simulation_ac_sweep_voltage_graph" simulation_ac_sweep_voltage_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_voltage_probe_id: string frequencies_hz: number[] complex_voltages: SimulationComplexSample[] @@ -33,15 +32,16 @@ export const simulation_ac_sweep_voltage_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), simulation_voltage_probe_id: z.string(), frequencies_hz: z.array(z.number()), complex_voltages: z.array(simulation_complex_sample), name: z.string().optional(), color: z.string().optional(), }) - .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.frequencies_hz.length === graph.complex_voltages.length, { diff --git a/src/simulation/simulation_dc_operating_point_current.ts b/src/simulation/simulation_dc_operating_point_current.ts index ef2e40ec..67c66b88 100644 --- a/src/simulation/simulation_dc_operating_point_current.ts +++ b/src/simulation/simulation_dc_operating_point_current.ts @@ -3,39 +3,38 @@ import { getZodPrefixedIdWithDefault } from "src/common" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcOperatingPointCurrent - extends SimulationParameterSweepResultCoordinates { +export interface SimulationDcOperatingPointCurrent { type: "simulation_dc_operating_point_current" simulation_dc_operating_point_current_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_current_probe_id: string current: number name?: string color?: string } -export const simulation_dc_operating_point_current = z - .object({ - type: z.literal("simulation_dc_operating_point_current"), - simulation_dc_operating_point_current_id: getZodPrefixedIdWithDefault( - "simulation_dc_operating_point_current", - ), - simulation_experiment_id: z.string(), - simulation_parameter_sweep_coordinate: - simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), - simulation_current_probe_id: z.string(), - current: z.number(), - name: z.string().optional(), - color: z.string().optional(), - }) - .superRefine(validateSimulationParameterSweepResultCoordinates) +export const simulation_dc_operating_point_current = z.object({ + type: z.literal("simulation_dc_operating_point_current"), + simulation_dc_operating_point_current_id: getZodPrefixedIdWithDefault( + "simulation_dc_operating_point_current", + ), + simulation_experiment_id: z.string(), + simulation_parameter_sweep_coordinate: + simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), + simulation_current_probe_id: z.string(), + current: z.number(), + name: z.string().optional(), + color: z.string().optional(), +}) export type SimulationDcOperatingPointCurrentInput = z.input< typeof simulation_dc_operating_point_current diff --git a/src/simulation/simulation_dc_operating_point_voltage.ts b/src/simulation/simulation_dc_operating_point_voltage.ts index e2f3bdd4..b3efd33d 100644 --- a/src/simulation/simulation_dc_operating_point_voltage.ts +++ b/src/simulation/simulation_dc_operating_point_voltage.ts @@ -3,39 +3,38 @@ import { getZodPrefixedIdWithDefault } from "src/common" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcOperatingPointVoltage - extends SimulationParameterSweepResultCoordinates { +export interface SimulationDcOperatingPointVoltage { type: "simulation_dc_operating_point_voltage" simulation_dc_operating_point_voltage_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_voltage_probe_id: string voltage: number name?: string color?: string } -export const simulation_dc_operating_point_voltage = z - .object({ - type: z.literal("simulation_dc_operating_point_voltage"), - simulation_dc_operating_point_voltage_id: getZodPrefixedIdWithDefault( - "simulation_dc_operating_point_voltage", - ), - simulation_experiment_id: z.string(), - simulation_parameter_sweep_coordinate: - simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), - simulation_voltage_probe_id: z.string(), - voltage: z.number(), - name: z.string().optional(), - color: z.string().optional(), - }) - .superRefine(validateSimulationParameterSweepResultCoordinates) +export const simulation_dc_operating_point_voltage = z.object({ + type: z.literal("simulation_dc_operating_point_voltage"), + simulation_dc_operating_point_voltage_id: getZodPrefixedIdWithDefault( + "simulation_dc_operating_point_voltage", + ), + simulation_experiment_id: z.string(), + simulation_parameter_sweep_coordinate: + simulation_parameter_sweep_coordinate.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), + simulation_voltage_probe_id: z.string(), + voltage: z.number(), + name: z.string().optional(), + color: z.string().optional(), +}) export type SimulationDcOperatingPointVoltageInput = z.input< typeof simulation_dc_operating_point_voltage diff --git a/src/simulation/simulation_dc_sweep_current_graph.ts b/src/simulation/simulation_dc_sweep_current_graph.ts index 7aede443..90345348 100644 --- a/src/simulation/simulation_dc_sweep_current_graph.ts +++ b/src/simulation/simulation_dc_sweep_current_graph.ts @@ -7,16 +7,15 @@ import { } from "./simulation_units" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcSweepCurrentGraph - extends SimulationParameterSweepResultCoordinates { +export interface SimulationDcSweepCurrentGraph { type: "simulation_dc_sweep_current_graph" simulation_dc_sweep_current_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_current_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -34,8 +33,10 @@ export const simulation_dc_sweep_current_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), simulation_current_probe_id: z.string(), sweep_values: z.array(z.number()), sweep_unit: simulation_dc_sweep_unit, @@ -43,7 +44,6 @@ export const simulation_dc_sweep_current_graph = z name: z.string().optional(), color: z.string().optional(), }) - .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.sweep_values.length === graph.current_levels.length, { diff --git a/src/simulation/simulation_dc_sweep_voltage_graph.ts b/src/simulation/simulation_dc_sweep_voltage_graph.ts index a9225757..4f125f64 100644 --- a/src/simulation/simulation_dc_sweep_voltage_graph.ts +++ b/src/simulation/simulation_dc_sweep_voltage_graph.ts @@ -7,16 +7,15 @@ import { } from "./simulation_units" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationDcSweepVoltageGraph - extends SimulationParameterSweepResultCoordinates { +export interface SimulationDcSweepVoltageGraph { type: "simulation_dc_sweep_voltage_graph" simulation_dc_sweep_voltage_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] simulation_voltage_probe_id: string sweep_values: number[] sweep_unit: SimulationDcSweepUnit @@ -34,8 +33,10 @@ export const simulation_dc_sweep_voltage_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), simulation_voltage_probe_id: z.string(), sweep_values: z.array(z.number()), sweep_unit: simulation_dc_sweep_unit, @@ -43,7 +44,6 @@ export const simulation_dc_sweep_voltage_graph = z name: z.string().optional(), color: z.string().optional(), }) - .superRefine(validateSimulationParameterSweepResultCoordinates) .refine( (graph) => graph.sweep_values.length === graph.voltage_levels.length, { diff --git a/src/simulation/simulation_measurement_result.ts b/src/simulation/simulation_measurement_result.ts index df102e60..71e9e529 100644 --- a/src/simulation/simulation_measurement_result.ts +++ b/src/simulation/simulation_measurement_result.ts @@ -2,7 +2,7 @@ import { z } from "zod" import { getZodPrefixedIdWithDefault } from "src/common" import { expectTypesMatch } from "src/utils/expect-types-match" import { - simulation_parameter_sweep_coordinate_set, + simulation_parameter_sweep_coordinate, type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" @@ -16,10 +16,6 @@ export interface SimulationMeasurementResult { simulation_parameter_sweep_coordinate_sets?: SimulationParameterSweepCoordinate[][] } -const finiteMeasurementValue = z.number().refine(Number.isFinite, { - message: "Measurement values must be finite", -}) - export const simulation_measurement_result = z .object({ type: z.literal("simulation_measurement_result"), @@ -28,10 +24,10 @@ export const simulation_measurement_result = z ), simulation_experiment_id: z.string(), name: z.string().min(1), - measurement_values: z.array(finiteMeasurementValue).min(1), + measurement_values: z.array(z.number().finite()).min(1), measurement_unit: z.string().min(1), simulation_parameter_sweep_coordinate_sets: z - .array(simulation_parameter_sweep_coordinate_set) + .array(z.array(simulation_parameter_sweep_coordinate).min(1)) .min(1) .optional(), }) @@ -57,37 +53,6 @@ export const simulation_measurement_result = z "measurement_values and simulation_parameter_sweep_coordinate_sets must have the same length", }) } - - const expectedParameterSweepIds = coordinateSets[0]?.map( - (coordinate) => coordinate.simulation_parameter_sweep_id, - ) - if (!expectedParameterSweepIds) return - - for (const [ - coordinateSetIndex, - coordinateSet, - ] of coordinateSets.entries()) { - const parameterSweepIds = coordinateSet.map( - (coordinate) => coordinate.simulation_parameter_sweep_id, - ) - const hasExpectedSweepOrder = - parameterSweepIds.length === expectedParameterSweepIds.length && - parameterSweepIds.every( - (parameterSweepId, sweepIndex) => - parameterSweepId === expectedParameterSweepIds[sweepIndex], - ) - if (!hasExpectedSweepOrder) { - context.addIssue({ - code: z.ZodIssueCode.custom, - path: [ - "simulation_parameter_sweep_coordinate_sets", - coordinateSetIndex, - ], - message: - "Every coordinate set must contain the same parameter sweeps in the same order", - }) - } - } }) .describe("Stores scalar measurements for a simulation experiment") diff --git a/src/simulation/simulation_parameter_sweep_coordinate.ts b/src/simulation/simulation_parameter_sweep_coordinate.ts index 5b0f9657..ec470584 100644 --- a/src/simulation/simulation_parameter_sweep_coordinate.ts +++ b/src/simulation/simulation_parameter_sweep_coordinate.ts @@ -12,13 +12,6 @@ export interface SimulationParameterSweepCoordinate { parameter_unit: SimulationParameterUnit } -export interface SimulationParameterSweepResultCoordinates { - /** Coordinate for a one-dimensional parameter sweep. */ - simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate - /** Ordered coordinates for a multidimensional parameter sweep. */ - simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] -} - export const simulation_parameter_sweep_coordinate = z.object({ simulation_parameter_sweep_id: z.string(), sweep_index: z.number().int().nonnegative(), @@ -26,50 +19,6 @@ export const simulation_parameter_sweep_coordinate = z.object({ parameter_unit: simulation_parameter_unit, }) -const rejectDuplicateParameterSweepIds = ( - coordinates: SimulationParameterSweepCoordinate[], - context: z.RefinementCtx, -) => { - const seenParameterSweepIds = new Set() - for (const [coordinateIndex, coordinate] of coordinates.entries()) { - if (seenParameterSweepIds.has(coordinate.simulation_parameter_sweep_id)) { - context.addIssue({ - code: z.ZodIssueCode.custom, - path: [coordinateIndex, "simulation_parameter_sweep_id"], - message: "Parameter sweep coordinate IDs must be unique", - }) - } - seenParameterSweepIds.add(coordinate.simulation_parameter_sweep_id) - } -} - -export const simulation_parameter_sweep_coordinate_set = z - .array(simulation_parameter_sweep_coordinate) - .min(1) - .superRefine(rejectDuplicateParameterSweepIds) - -export const simulation_parameter_sweep_coordinates = z - .array(simulation_parameter_sweep_coordinate) - .min(2) - .superRefine(rejectDuplicateParameterSweepIds) - -export const validateSimulationParameterSweepResultCoordinates = ( - result: SimulationParameterSweepResultCoordinates, - context: z.RefinementCtx, -) => { - if ( - result.simulation_parameter_sweep_coordinate && - result.simulation_parameter_sweep_coordinates - ) { - context.addIssue({ - code: z.ZodIssueCode.custom, - path: ["simulation_parameter_sweep_coordinates"], - message: - "Use the singular coordinate for one-dimensional sweeps or the plural coordinates for multidimensional sweeps, not both", - }) - } -} - expectTypesMatch< SimulationParameterSweepCoordinate, z.infer diff --git a/src/simulation/simulation_transient_current_graph.ts b/src/simulation/simulation_transient_current_graph.ts index da8af36f..9bd801a7 100644 --- a/src/simulation/simulation_transient_current_graph.ts +++ b/src/simulation/simulation_transient_current_graph.ts @@ -4,16 +4,15 @@ import { duration_ms, ms } from "src/units" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationTransientCurrentGraph - extends SimulationParameterSweepResultCoordinates { +export interface SimulationTransientCurrentGraph { type: "simulation_transient_current_graph" simulation_transient_current_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] timestamps_ms?: number[] current_levels: number[] source_component_id?: string @@ -34,8 +33,10 @@ export const simulation_transient_current_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), timestamps_ms: z.array(z.number()).optional(), current_levels: z.array(z.number()), source_component_id: z.string().optional(), @@ -46,7 +47,6 @@ export const simulation_transient_current_graph = z name: z.string().optional(), color: z.string().optional(), }) - .superRefine(validateSimulationParameterSweepResultCoordinates) .describe("Stores current measurements over time for a simulation") export type SimulationTransientCurrentGraphInput = z.input< diff --git a/src/simulation/simulation_transient_voltage_graph.ts b/src/simulation/simulation_transient_voltage_graph.ts index 60b692cc..8f27652f 100644 --- a/src/simulation/simulation_transient_voltage_graph.ts +++ b/src/simulation/simulation_transient_voltage_graph.ts @@ -4,16 +4,15 @@ import { duration_ms, ms } from "src/units" import { expectTypesMatch } from "src/utils/expect-types-match" import { simulation_parameter_sweep_coordinate, - simulation_parameter_sweep_coordinates, - type SimulationParameterSweepResultCoordinates, - validateSimulationParameterSweepResultCoordinates, + type SimulationParameterSweepCoordinate, } from "./simulation_parameter_sweep_coordinate" -export interface SimulationTransientVoltageGraph - extends SimulationParameterSweepResultCoordinates { +export interface SimulationTransientVoltageGraph { type: "simulation_transient_voltage_graph" simulation_transient_voltage_graph_id: string simulation_experiment_id: string + simulation_parameter_sweep_coordinate?: SimulationParameterSweepCoordinate + simulation_parameter_sweep_coordinates?: SimulationParameterSweepCoordinate[] timestamps_ms?: number[] voltage_levels: number[] source_component_id?: string @@ -34,8 +33,10 @@ export const simulation_transient_voltage_graph = z simulation_experiment_id: z.string(), simulation_parameter_sweep_coordinate: simulation_parameter_sweep_coordinate.optional(), - simulation_parameter_sweep_coordinates: - simulation_parameter_sweep_coordinates.optional(), + simulation_parameter_sweep_coordinates: z + .array(simulation_parameter_sweep_coordinate) + .min(2) + .optional(), timestamps_ms: z.array(z.number()).optional(), voltage_levels: z.array(z.number()), source_component_id: z.string().optional(), @@ -46,7 +47,6 @@ export const simulation_transient_voltage_graph = z name: z.string().optional(), color: z.string().optional(), }) - .superRefine(validateSimulationParameterSweepResultCoordinates) .describe("Stores voltage measurements over time for a simulation") export type SimulationTransientVoltageGraphInput = z.input< diff --git a/tests/simulation_analysis_types.test.ts b/tests/simulation_analysis_types.test.ts index 347000bb..d09fee4e 100644 --- a/tests/simulation_analysis_types.test.ts +++ b/tests/simulation_analysis_types.test.ts @@ -125,36 +125,3 @@ test("parses multidimensional sweep coordinates on analysis results", () => { expect(result.simulation_parameter_sweep_coordinates).toHaveLength(2) }) - -test("rejects singular and multidimensional sweep coordinates together", () => { - expect(() => - simulation_dc_operating_point_voltage.parse({ - type: "simulation_dc_operating_point_voltage", - simulation_experiment_id: "simulation_experiment_0", - simulation_voltage_probe_id: "simulation_voltage_probe_0", - voltage: 3.3, - simulation_parameter_sweep_coordinate: { - simulation_parameter_sweep_id: "simulation_parameter_sweep_voltage", - sweep_index: 1, - parameter_value: 3.6, - parameter_unit: "V", - }, - simulation_parameter_sweep_coordinates: [ - { - simulation_parameter_sweep_id: "simulation_parameter_sweep_voltage", - sweep_index: 1, - parameter_value: 3.6, - parameter_unit: "V", - }, - { - simulation_parameter_sweep_id: "simulation_parameter_sweep_current", - sweep_index: 4, - parameter_value: 1, - parameter_unit: "A", - }, - ], - }), - ).toThrow( - "Use the singular coordinate for one-dimensional sweeps or the plural coordinates for multidimensional sweeps, not both", - ) -}) diff --git a/tests/simulation_measurement_result.test.ts b/tests/simulation_measurement_result.test.ts index a875fc40..cc647a00 100644 --- a/tests/simulation_measurement_result.test.ts +++ b/tests/simulation_measurement_result.test.ts @@ -113,46 +113,6 @@ test("rejects multiple values when there are no parameter sweeps", () => { ) }) -test("rejects coordinate sets with inconsistent sweep order", () => { - expect(() => - simulation_measurement_result.parse({ - type: "simulation_measurement_result", - simulation_experiment_id: "simulation_experiment_load_regulation", - name: "output-voltage-regulation", - measurement_values: [-0.08, -0.07], - measurement_unit: "%", - simulation_parameter_sweep_coordinate_sets: [ - [inputVoltageCoordinate, loadCurrentCoordinate], - [loadCurrentCoordinate, inputVoltageCoordinate], - ], - }), - ).toThrow( - "Every coordinate set must contain the same parameter sweeps in the same order", - ) -}) - -test("rejects duplicate sweep IDs in one coordinate set", () => { - expect(() => - simulation_measurement_result.parse({ - type: "simulation_measurement_result", - simulation_experiment_id: "simulation_experiment_load_regulation", - name: "output-voltage-regulation", - measurement_values: [-0.08], - measurement_unit: "%", - simulation_parameter_sweep_coordinate_sets: [ - [ - inputVoltageCoordinate, - { - ...inputVoltageCoordinate, - parameter_value: 3.6, - sweep_index: 1, - }, - ], - ], - }), - ).toThrow("Parameter sweep coordinate IDs must be unique") -}) - test("rejects non-finite measurement values", () => { expect(() => simulation_measurement_result.parse({