diff --git a/README.md b/README.md index 5e754b5e..fb8ea49e 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 @@ -3451,6 +3475,7 @@ interface SimulationAcSweepCurrentGraph { 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[] @@ -3469,6 +3494,7 @@ interface SimulationAcSweepVoltageGraph { 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[] @@ -3591,6 +3617,7 @@ interface SimulationDcOperatingPointCurrent { 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 @@ -3608,6 +3635,7 @@ interface SimulationDcOperatingPointVoltage { 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 @@ -3625,6 +3653,7 @@ interface SimulationDcSweepCurrentGraph { 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 @@ -3644,6 +3673,7 @@ interface SimulationDcSweepVoltageGraph { 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 @@ -3688,6 +3718,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) @@ -3839,6 +3885,7 @@ interface SimulationTransientCurrentGraph { 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 @@ -3861,6 +3908,7 @@ interface SimulationTransientVoltageGraph { 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/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..b3d9cf1e 100644 --- a/src/simulation/simulation_ac_sweep_current_graph.ts +++ b/src/simulation/simulation_ac_sweep_current_graph.ts @@ -15,6 +15,7 @@ export interface SimulationAcSweepCurrentGraph { 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[] @@ -31,6 +32,10 @@ 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: 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), diff --git a/src/simulation/simulation_ac_sweep_voltage_graph.ts b/src/simulation/simulation_ac_sweep_voltage_graph.ts index 783987d6..5086e688 100644 --- a/src/simulation/simulation_ac_sweep_voltage_graph.ts +++ b/src/simulation/simulation_ac_sweep_voltage_graph.ts @@ -15,6 +15,7 @@ export interface SimulationAcSweepVoltageGraph { 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[] @@ -31,6 +32,10 @@ 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: 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), diff --git a/src/simulation/simulation_dc_operating_point_current.ts b/src/simulation/simulation_dc_operating_point_current.ts index 329cebc0..67c66b88 100644 --- a/src/simulation/simulation_dc_operating_point_current.ts +++ b/src/simulation/simulation_dc_operating_point_current.ts @@ -11,6 +11,7 @@ export interface SimulationDcOperatingPointCurrent { 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 @@ -25,6 +26,10 @@ export const simulation_dc_operating_point_current = z.object({ 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(), diff --git a/src/simulation/simulation_dc_operating_point_voltage.ts b/src/simulation/simulation_dc_operating_point_voltage.ts index b49dfa52..b3efd33d 100644 --- a/src/simulation/simulation_dc_operating_point_voltage.ts +++ b/src/simulation/simulation_dc_operating_point_voltage.ts @@ -11,6 +11,7 @@ export interface SimulationDcOperatingPointVoltage { 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 @@ -25,6 +26,10 @@ export const simulation_dc_operating_point_voltage = z.object({ 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(), diff --git a/src/simulation/simulation_dc_sweep_current_graph.ts b/src/simulation/simulation_dc_sweep_current_graph.ts index 9f14f897..90345348 100644 --- a/src/simulation/simulation_dc_sweep_current_graph.ts +++ b/src/simulation/simulation_dc_sweep_current_graph.ts @@ -15,6 +15,7 @@ export interface SimulationDcSweepCurrentGraph { 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 @@ -32,6 +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: 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, diff --git a/src/simulation/simulation_dc_sweep_voltage_graph.ts b/src/simulation/simulation_dc_sweep_voltage_graph.ts index 2ab80d38..4f125f64 100644 --- a/src/simulation/simulation_dc_sweep_voltage_graph.ts +++ b/src/simulation/simulation_dc_sweep_voltage_graph.ts @@ -15,6 +15,7 @@ export interface SimulationDcSweepVoltageGraph { 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 @@ -32,6 +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: 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, diff --git a/src/simulation/simulation_measurement_result.ts b/src/simulation/simulation_measurement_result.ts new file mode 100644 index 00000000..71e9e529 --- /dev/null +++ b/src/simulation/simulation_measurement_result.ts @@ -0,0 +1,69 @@ +import { z } from "zod" +import { getZodPrefixedIdWithDefault } from "src/common" +import { expectTypesMatch } from "src/utils/expect-types-match" +import { + simulation_parameter_sweep_coordinate, + 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[][] +} + +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(z.number().finite()).min(1), + measurement_unit: z.string().min(1), + simulation_parameter_sweep_coordinate_sets: z + .array(z.array(simulation_parameter_sweep_coordinate).min(1)) + .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", + }) + } + }) + .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_transient_current_graph.ts b/src/simulation/simulation_transient_current_graph.ts index cd423516..9bd801a7 100644 --- a/src/simulation/simulation_transient_current_graph.ts +++ b/src/simulation/simulation_transient_current_graph.ts @@ -12,6 +12,7 @@ export interface SimulationTransientCurrentGraph { 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 @@ -32,6 +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: 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(), diff --git a/src/simulation/simulation_transient_voltage_graph.ts b/src/simulation/simulation_transient_voltage_graph.ts index 8a732bb9..8f27652f 100644 --- a/src/simulation/simulation_transient_voltage_graph.ts +++ b/src/simulation/simulation_transient_voltage_graph.ts @@ -12,6 +12,7 @@ export interface SimulationTransientVoltageGraph { 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 @@ -32,6 +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: 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(), diff --git a/tests/simulation_analysis_types.test.ts b/tests/simulation_analysis_types.test.ts index c3ff2fad..d09fee4e 100644 --- a/tests/simulation_analysis_types.test.ts +++ b/tests/simulation_analysis_types.test.ts @@ -100,3 +100,28 @@ 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) +}) diff --git a/tests/simulation_measurement_result.test.ts b/tests/simulation_measurement_result.test.ts new file mode 100644 index 00000000..cc647a00 --- /dev/null +++ b/tests/simulation_measurement_result.test.ts @@ -0,0 +1,126 @@ +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 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() +})