# Simulation Workflow Monata separates simulation setup from simulation execution. A simulation is described as data, submitted to an executor, then returned as a `SimResult`. ```text circuit + AnalysisSpec + optional corner/parameters -> SimTask -> Executor -> SimResult ``` ## Analysis Specifications Analysis specs describe what to run. Creating a spec does not start a simulation. ```python from monata.sim.core import ACSpec, DCSpec, NoiseSpec, OPSpec, TranSpec tran = TranSpec(stop=10e-9, step=10e-12) ac = ACSpec(start=1e3, stop=1e9, points=100, variation="dec") dc = DCSpec(source="Vin", start=0.0, stop=1.2, step=0.01) op = OPSpec() noise = NoiseSpec(output_node="out", input_source="Vin", start=1e3, stop=1e9, points=100) ``` The built-in `ngspice-subprocess` backend currently executes `DCSpec`, `TranSpec`, `ACSpec`, `OPSpec`, and `NoiseSpec`. Operating-point outputs use task-level `output_names`; noise outputs use `NoiseSpec.output_node` and `NoiseSpec.input_source`. ## Simulation Tasks `SimTask` is the unit of work. It combines the circuit, analysis, backend, and optional context such as corners or parameter overrides. ```python from monata.sim.core import SimTask task = SimTask( circuit=circuit, analysis_spec=tran, simulator="ngspice-subprocess", output_names=["in", "out"], param_overrides={"w_p": 2e-6}, osdi_paths=["models/bsimcmg.osdi"], metadata={"run": "nominal"}, ) ``` For native ngspice execution, `circuit` must be a `monata.netlist.Circuit`. `output_names` is required for DC, transient, AC, and operating-point runs: the backend does not guess or enumerate nodes. DC and transient runs write `v(name)` vectors; AC runs write `mag(v(name))` vectors; operating-point runs return one-row arrays for requested node voltages. Noise is the exception to the task-level output rule. A `NoiseSpec` already names the output node and input source, so `SimTask.output_names` must be empty. Noise results return `onoise_spectrum` and `inoise_spectrum` waveforms aligned to `sweep_var`, with `onoise_total` and `inoise_total` in metadata. The input source must have an AC value in the netlist. `osdi_paths` are loaded with ngspice `pre_osdi` commands before the analysis command. `param_overrides` supports circuit-level `.param` overrides whose names are simple identifiers, plus structured native element overrides written as `element.parameter` targets such as `R1.R` or `M1.W`. A structured target must match an element in the native intermediate representation; otherwise the backend fails explicitly with `metadata["reason"] == "unsupported_param_overrides"`. Corner voltage overrides are applied through the same structured mutation path when the named source exists, for example `{"VDD": 1.0}` becomes a `VDD.V` source-value override. Process/model corner fields still require concrete model files or techlib projection before ngspice can execute them. ## Executors `LocalExecutor` dispatches tasks on the local machine. Use one task for a single run, or map many tasks for sweeps and corners. ```python from monata.sim.core import LocalExecutor executor = LocalExecutor(max_workers=4) future = executor.submit(task) result = future.result() ``` For multiple independent tasks: ```python futures = executor.map(tasks) results = [future.result() for future in futures] ``` ## Results `SimResult` contains: | Field | Meaning | | --- | --- | | `status` | `"ok"` or `"failed"` | | `waveforms` | named waveform arrays or backend output objects | | `sweep_var` | x-axis values for swept data when available | | `corner` | corner metadata when the run came from a corner matrix | | `metadata` | backend-specific details | | `error_message` | failure text when the task failed | Always check `status` before evaluating measurements. ```python if result.status != "ok": raise RuntimeError(result.error_message) vout = result.waveforms["out"] ``` Common ngspice failure reasons include: | Reason | Meaning | | --- | --- | | `simulator_missing` | `ngspice` could not be found on `PATH` or under `CONDA_PREFIX/bin` | | `unsupported_analysis` | the selected analysis is not implemented by the native backend | | `no_outputs_requested` | `output_names` was empty for an analysis that requires task-level outputs | | `invalid_task` | an output name, analysis field, OSDI path, or command token was invalid | | `model_missing` | an OSDI or corner model file path did not exist | | `unsupported_param_overrides` | a parameter or source override target was not found or could not be applied safely | | `unsupported_corner` | a process/model corner field could not be projected to a concrete ngspice model reference | | `subprocess_failed` | ngspice ran but exited non-zero | | `parser_failed` | ngspice output could not be parsed into the requested outputs | ## Backend Selection Keep backend selection close to the task or library technology configuration: ```python task = SimTask( circuit=circuit, analysis_spec=spec, simulator="ngspice-subprocess", ) ``` `SimTask.simulator` is resolved through Monata's backend registry. Use `ngspice-subprocess` as the default stable local runner. Use `ngspice-shared` only when a compatible user-installed `libngspice` shared library is available in the runtime environment. Both runners preserve the same `SimTask` and `SimResult` contract and keep ngspice itself as an external runtime dependency. Use the user guide for Monata concepts, and use the toolchain pages for backend-specific behavior: - {doc}`../toolchain/ngspice`