diff --git a/README.md b/README.md index 2965d6f5..9d2c41cd 100644 --- a/README.md +++ b/README.md @@ -232,10 +232,11 @@ export interface AmmeterProps< ```ts export interface AnalogSimulationProps { name?: string; - simulationType?: "spice_transient_analysis"; + simulationType?: "spice_dc_operating_point" | "spice_transient_analysis"; duration?: number | string; startTime?: number | string; timePerStep?: number | string; + timeout?: number | string; spiceEngine?: AutocompleteString<"spicey" | "ngspice">; spiceOptions?: SpiceOptions; graphIndependentAxes?: boolean; @@ -448,6 +449,12 @@ export interface ChipPropsSU< */ noConnect?: readonly PinLabel[] | PinLabel[]; connections?: Connections; + /** + * Treat this chip as the intentional boundary of an analog simulation. + * Defaults to false, allowing an accidentally missing SPICE model to be + * reported instead of silently omitting the chip. + */ + simulationBoundary?: boolean; spiceModel?: SpiceModelElement; } ``` @@ -718,11 +725,7 @@ export type FabricationNoteRectProps = z.input; export interface FabricationNoteTextProps extends PcbLayoutProps { text: string; anchorAlignment?: - | "center" - | "top_left" - | "top_right" - | "bottom_left" - | "bottom_right"; + "center" | "top_left" | "top_right" | "bottom_left" | "bottom_right"; font?: "tscircuit2024"; fontSize?: string | number; color?: string; @@ -1288,11 +1291,7 @@ export interface PcbNoteRectProps extends Omit { export interface PcbNoteTextProps extends PcbLayoutProps { text: string; anchorAlignment?: - | "center" - | "top_left" - | "top_right" - | "bottom_left" - | "bottom_right"; + "center" | "top_left" | "top_right" | "bottom_left" | "bottom_right"; font?: "tscircuit2024"; fontSize?: string | number; color?: string; @@ -2126,7 +2125,6 @@ export interface PlatformConfig { ``` [Source](https://github.com/tscircuit/props/blob/main/lib/platformConfig.ts) - @@ -2150,5 +2148,4 @@ export interface ProjectConfig extends Pick< ``` [Source](https://github.com/tscircuit/props/blob/main/lib/projectConfig.ts) - diff --git a/generated/COMPONENT_TYPES.md b/generated/COMPONENT_TYPES.md index ff6bec62..fab2fed5 100644 --- a/generated/COMPONENT_TYPES.md +++ b/generated/COMPONENT_TYPES.md @@ -902,10 +902,11 @@ export const ammeterProps = commonComponentProps.extend({ ```typescript export interface AnalogSimulationProps { name?: string - simulationType?: "spice_transient_analysis" + simulationType?: "spice_dc_operating_point" | "spice_transient_analysis" duration?: number | string startTime?: number | string timePerStep?: number | string + timeout?: number | string spiceEngine?: AutocompleteString<"spicey" | "ngspice"> spiceOptions?: SpiceOptions graphIndependentAxes?: boolean @@ -919,11 +920,12 @@ export interface SpiceOptions { export const analogSimulationProps = z.object({ name: z.string().optional(), simulationType: z - .literal("spice_transient_analysis") + .enum(["spice_dc_operating_point", "spice_transient_analysis"]) .default("spice_transient_analysis"), duration: ms.optional(), startTime: ms.optional(), timePerStep: ms.optional(), + timeout: ms.optional(), spiceEngine: spiceEngine.optional(), spiceOptions: spiceOptions.optional(), graphIndependentAxes: z.boolean().optional(), @@ -1193,6 +1195,7 @@ export interface ChipPropsSU< externallyConnectedPins?: string[][] noConnect?: readonly PinLabel[] | PinLabel[] connections?: Connections + simulationBoundary?: boolean spiceModel?: SpiceModelElement } /** @@ -1235,6 +1238,7 @@ export const chipProps = commonComponentProps.extend({ noSchematicRepresentation: z.boolean().optional(), noConnect: noConnectProp.optional(), connections: connectionsProp.optional(), + simulationBoundary: z.boolean().default(false), spiceModel: spicemodelElement.optional(), }) ``` diff --git a/generated/PROPS_OVERVIEW.md b/generated/PROPS_OVERVIEW.md index 7b0c00c9..49e531eb 100644 --- a/generated/PROPS_OVERVIEW.md +++ b/generated/PROPS_OVERVIEW.md @@ -31,10 +31,11 @@ export interface AmmeterProps export interface AnalogSimulationProps { name?: string - simulationType?: "spice_transient_analysis" + simulationType?: "spice_dc_operating_point" | "spice_transient_analysis" duration?: number | string startTime?: number | string timePerStep?: number | string + timeout?: number | string spiceEngine?: AutocompleteString<"spicey" | "ngspice"> spiceOptions?: SpiceOptions graphIndependentAxes?: boolean @@ -448,6 +449,12 @@ export interface ChipPropsSU< */ noConnect?: readonly PinLabel[] | PinLabel[] connections?: Connections + /** + * Treat this chip as the intentional boundary of an analog simulation. + * Defaults to false, allowing an accidentally missing SPICE model to be + * reported instead of silently omitting the chip. + */ + simulationBoundary?: boolean spiceModel?: SpiceModelElement } @@ -2123,7 +2130,15 @@ export interface SolderJumperProps extends JumperProps { export interface SpiceEngine { - simulate: (spiceString: string) => Promise + simulate: ( + spiceString: string, + options?: SpiceEngineSimulationOptions, + ) => Promise +} + + +export interface SpiceEngineSimulationOptions { + timeoutMs?: number } diff --git a/lib/components/analogsimulation.ts b/lib/components/analogsimulation.ts index c587da4c..4f311b61 100644 --- a/lib/components/analogsimulation.ts +++ b/lib/components/analogsimulation.ts @@ -5,10 +5,11 @@ import { z } from "zod" export interface AnalogSimulationProps { name?: string - simulationType?: "spice_transient_analysis" + simulationType?: "spice_dc_operating_point" | "spice_transient_analysis" duration?: number | string startTime?: number | string timePerStep?: number | string + timeout?: number | string spiceEngine?: AutocompleteString<"spicey" | "ngspice"> spiceOptions?: SpiceOptions graphIndependentAxes?: boolean @@ -35,11 +36,12 @@ const spiceOptions = z.object({ export const analogSimulationProps = z.object({ name: z.string().optional(), simulationType: z - .literal("spice_transient_analysis") + .enum(["spice_dc_operating_point", "spice_transient_analysis"]) .default("spice_transient_analysis"), duration: ms.optional(), startTime: ms.optional(), timePerStep: ms.optional(), + timeout: ms.optional(), spiceEngine: spiceEngine.optional(), spiceOptions: spiceOptions.optional(), graphIndependentAxes: z.boolean().optional(), diff --git a/lib/components/chip.ts b/lib/components/chip.ts index e756f85d..02757dca 100644 --- a/lib/components/chip.ts +++ b/lib/components/chip.ts @@ -71,6 +71,12 @@ export interface ChipPropsSU< */ noConnect?: readonly PinLabel[] | PinLabel[] connections?: Connections + /** + * Treat this chip as the intentional boundary of an analog simulation. + * Defaults to false, allowing an accidentally missing SPICE model to be + * reported instead of silently omitting the chip. + */ + simulationBoundary?: boolean spiceModel?: SpiceModelElement } @@ -172,6 +178,7 @@ export const chipProps = commonComponentProps.extend({ noSchematicRepresentation: z.boolean().optional(), noConnect: noConnectProp.optional(), connections: connectionsProp.optional(), + simulationBoundary: z.boolean().default(false), spiceModel: spicemodelElement.optional(), }) diff --git a/lib/platformConfig.ts b/lib/platformConfig.ts index 12d1a1c7..525d1a8c 100644 --- a/lib/platformConfig.ts +++ b/lib/platformConfig.ts @@ -27,8 +27,15 @@ export interface SpiceEngineSimulationResult { simulationResultCircuitJson: CircuitJson } +export interface SpiceEngineSimulationOptions { + timeoutMs?: number +} + export interface SpiceEngine { - simulate: (spiceString: string) => Promise + simulate: ( + spiceString: string, + options?: SpiceEngineSimulationOptions, + ) => Promise } export type SimpleRouteJson = any diff --git a/tests/analogsimulation.test.ts b/tests/analogsimulation.test.ts index 6a09f8aa..14dfb450 100644 --- a/tests/analogsimulation.test.ts +++ b/tests/analogsimulation.test.ts @@ -41,6 +41,20 @@ test("analog simulation accepts time parameters", () => { expect(parsed.timePerStep).toBe(0.001) }) +test("analog simulation accepts DC operating-point analysis and timeout", () => { + const raw: AnalogSimulationProps = { + name: "bias", + simulationType: "spice_dc_operating_point", + timeout: "2s", + } + + expectTypeOf(raw).toMatchTypeOf>() + + const parsed = analogSimulationProps.parse(raw) + expect(parsed.simulationType).toBe("spice_dc_operating_point") + expect(parsed.timeout).toBe(2000) +}) + test("analog simulation accepts spice engine selection", () => { const raw: AnalogSimulationProps = { spiceEngine: "spicey", diff --git a/tests/spicemodel.test.tsx b/tests/spicemodel.test.tsx index 8731110e..3862a868 100644 --- a/tests/spicemodel.test.tsx +++ b/tests/spicemodel.test.tsx @@ -51,6 +51,16 @@ test("chip spiceModel accepts a spicemodel element", () => { expect(chipProps.parse(raw).spiceModel).toBe(raw.spiceModel) }) +test("chip accepts an explicit analog simulation boundary", () => { + const raw: ChipProps = { + name: "U1", + simulationBoundary: true, + } + + expect(chipProps.parse(raw).simulationBoundary).toBe(true) + expect(chipProps.parse({ name: "U2" }).simulationBoundary).toBe(false) +}) + test("chip spiceModel rejects plain model objects", () => { expect(() => { chipProps.parse({