PEARSim
A headless, deterministic power-electronics simulator you drive from a text file, a command line or an AI agent. Models are YAML, runs are directories of hash-manifested artifacts, and every number the tool prints can be traced back to the model, the request and the solver settings that produced it. Published by PEAR Power.
Contents: Install · Five minutes · From an AI agent · What it simulates · How results are proven · What it does not do · Reproducibility · Working with it · Contributing
Why PEARSim
- Evidence kept, not asserted. Results are checked against closed forms written independently of the solver, against an open second engine (ngspice), and against per-run physics invariants such as the energy balance. The comparison records live in the repository and anyone can regenerate them.
- It refuses instead of guessing. Unknown capabilities are treated as unsupported,
invalid circuits fail with a named error instead of hidden regularization, and every
result carries its warnings and a confidence level. A capability registry
(
pear-sim capabilities) states what is supported, experimental or unsupported. - Built for agents as much as for people. The same contract runs through the CLI, a typed Python API and an MCP server, so an AI agent can write, validate and simulate models and read back compact, auditable results.
Install
Once released:
pip install pear-sim # the simulator: CLI and Python API
pip install "pear-sim[mcp]" # plus the MCP server (pear-sim mcp)
uvx pear-sim-mcp # or run the MCP server without installing anything
Requires Python 3.11+ on Linux, macOS or Windows. Runtime dependencies are numpy, scipy, PyYAML and jsonschema — no Docker, no browser, no proprietary simulator.
Five minutes
pear-sim lint examples/buck.yaml # validate model + topology
pear-sim explain examples/buck.yaml --json # resolved values, assumptions, limits
pear-sim simulate examples/buck.yaml --out runs/buck # portable run bundle
pear-sim report runs/buck --out reports/buck # offline HTML, no network
(From a source checkout, prefix the commands with uv run.)
The report is a single offline HTML file: run metadata and hashes, every warning, the waveforms (each fast-switching signal also drawn as an undecimated zoom, so PWM stays visible), the physics audit, switching timeline, losses and junction temperatures when device cards are declared, and provenance.
From an AI agent (MCP)
PEARSim ships an MCP server, so Claude, Codex, Cursor or any other MCP client can write, validate and simulate models for you:
claude mcp add pear-sim -- uvx pear-sim-mcp # after the PyPI release
claude mcp add pear-sim -- uv run --project /path/to/pear-sim pear-sim mcp # from a checkout
Run it from your own project directory: that is the directory the server may read models
from, and its runs go to .pear-sim/ there. (Use --project, not --directory: the
latter would move the server into the PEARSim checkout.)
Then ask, for example, "Design a 48 V to 12 V, 10 A synchronous buck at 200 kHz and check the output ripple". The agent starts from a shipped example, validates the model, simulates it and reports the numbers together with the tool's warnings — in our agent evaluation Claude Opus 5.5 passed 33 of 33 graded runs (11 engineering tasks × 3), with every quoted number checked against the simulator. Configuration for other clients, the tool list and the safety rules (closed filesystem boundary, time limits, compact results): agent interface.
A buck in full, so you can see the whole format at once:
schema: pear-sim/v0.1
name: buck_ccm
parameters:
vin: 100 V
fsw: 20 kHz
duty: 40 %
load: 10 ohm
circuit:
ground: gnd
components:
- {id: vin, type: voltage_source, p: in, n: gnd, value: "${vin}"}
- {id: q1, type: ideal_switch, p: in, n: sw, control: pwm, initial: on}
- {id: d1, type: diode, p: gnd, n: sw, initial: off}
- {id: l1, type: inductor, p: sw, n: out, value: 1 mH, initial_current: 4 A}
- {id: c1, type: capacitor, p: out, n: gnd, value: 100 uF, initial_voltage: 40 V}
- {id: load, type: resistor, p: out, n: gnd, value: "${load}"}
controls:
pwm: {type: pwm, frequency: "${fsw}", duty: "${duty}", phase: 0 s}
analysis:
transient: {start: 0 s, stop: 2 ms, output_step: 1 us, solver: dopri5, rtol: 1.0e-8, atol: 1.0e-10}
outputs: [v(out), i(l1), i(q1), i(d1)]
Units are explicit and checked (100 uF, 20 kHz, 40 %). There is no expression
language: ${1/fsw} is an error, not a feature — a value you cannot name is a value you
cannot audit. Full contract: model format.
What it simulates today
Kernel. Event-driven piecewise-LTI state space (modified nodal analysis with a diode active set). Switching instants are located, not stepped over: scheduled gate edges and natural commutations both become exact events, and capacitor voltages and inductor currents stay continuous across them.
| Area | What works | Example to start from |
|---|---|---|
| DC-DC | buck, boost, inverting buck-boost in CCM/BCM/DCM with natural diode turn-off | examples/buck.yaml, boost.yaml, buck_dcm.yaml |
| Isolated | DAB (phase shift), half-bridge LLC with resonant tank | dab_phase_shift.yaml, llc_resonant.yaml |
| Rectifiers | half-wave, 6-pulse on a transformer, 12-pulse Y-y-d with coupled winding groups, phase-controlled thyristor | rectifier_12pulse_yyd.yaml, thyristor_phase_controlled.yaml |
| Inverters | three-phase two-level (six-step, SPWM, SVPWM), floating neutral, weak grid, grid-following with PLL in the loop | inverter_three_phase_spwm.yaml, grid_following_converter.yaml |
| Multilevel | three-level NPC and five-level DNPC, three-phase, incl. DC-midpoint balance | npc_three_phase.yaml, npc5_three_phase.yaml |
| Machines | PMSM (sinusoidal back-EMF), salient-pole and synchronous reluctance machines, flux maps ψ(i_d, i_q), DC drive with H-bridge (machines are experimental) | pmsm_inverter_drive.yaml, synrm_torque_test.yaml, dc_drive_hbridge.yaml |
| Magnetics | mutual coupling, coupled winding groups (3×3 and larger), saturable inductor | transformer_rl.yaml, rl_sat_inductor.yaml |
| Devices | ideal or piecewise-linear switches and diodes (on_resistance, forward_voltage), MOSFET as switch + body diode |
sync_buck_mosfet.yaml |
| Control | fixed PWM, complementary PWM with dead time, three-phase SPWM/SVPWM, level-shifted NPC modulation, sampled PI and dq current loops, a signal-block layer with a pi block |
synchronous_buck_complementary.yaml, buck_closed_loop_signals.yaml |
| Losses & thermal | device cards (conduction LUT, switching energies), Foster networks, air and liquid cooling, efficiency map, rainflow counting | buck_with_loss_cards.yaml, traction_inverter_liquid_cooled.yaml |
Analyses beyond a single transient: periodic steady state by Newton shooting
(including inductor-star topologies), small-signal loop gain with phase/gain margins for
a declared compensator — with the ZOH and computation-delay term, because a continuous
Bode plot on a digitally controlled converter is optimistic by tens of degrees — modal
resonance audit, event-aligned Fourier/THD, bounded parameter and tolerance-corner
sweeps. Sweeps run their points in parallel (--jobs, default all cores but one); the
retained results do not depend on the job count.
Many designs at once. pear-sim batch screens up to 2 000 designs (Latin hypercube,
grid or a list), ranks them against a declared objective and constraints (for example:
lowest device losses with output ripple under 0.5 V and every junction under 125 °C) and
re-runs the finalists in full, checking they reproduce the screening numbers. The winner
is the best of the sample, not an optimum. See docs/batch.md.
An optional compiled kernel in Rust (--backend rust, docs/rust-core.md)
integrates segments with a step-for-step port of SciPy's RK45; the Python engine stays
the reference and every covered example is checked against it in CI.
pear-sim sensitivity answers which parameters move which outputs over declared ranges:
Morris screening or Sobol indices, each with a bootstrap 95 % interval
(docs/sensitivity.md).
Reuse. Declared subcircuits let you write a bridge once and instantiate it: the
twelve-pulse rectifier is one three_phase_diode_bridge definition used twice. Expansion
is deterministic and happens at load time, so hierarchy is presentation and never
physics — a flat and a hierarchical spelling of the same circuit produce the same physics
hash and bit-identical waveforms.
How results are proven
This is the part the project exists for. Every claim above sits on one of three independent classes of evidence, and the evidence is kept, not asserted:
- Closed forms. Analytic RC/RL/RLC transients, conversion ratios, Foster impedance, R-L phase-controlled rectifier extinction angles, canonical periodic oracles for buck/boost/inverting buck-boost written independently of the solver (the oracle layer never imports the kernel).
- A second, open engine. An ngspice differential adapter over the linear subset,
with a relative budget and the resampling term declared explicitly. Anyone can rerun
it: install ngspice and run
uv run pytest tests/test_ngspice_oracle.py. - Physics invariants. Per-run energy balance, KCL/KVL structure, mode-coverage observations, and analytic self-checks on recognized topologies.
Comparison records live in validation/oracle-records/:
each carries the deviation actually measured against the tolerance declared, and every
record must be regenerable by a test in this repository. Agreement between two simulators
is verification (the implementation solves the model it claims to), not validation
against hardware — no claim here rests on measurements. Where a capability has no
independent comparison at all, its capability entry says so.
Machine-readable status for every capability, including the unsupported ones:
uv run pear-sim capabilities --json
uv run pear-sim capabilities --id analysis.steady_state.shooting
What it deliberately does not do
Read this before trusting a number.
- Piecewise-linear devices at best. A switch or diode is ideal unless you give it
on_resistance/forward_voltage; then conduction isv = V_f + R_on·iin the circuit equations. There is no reverse recovery, no switching transient, no temperature dependence and no EMI, and no parasitics unless you declare them as circuit elements (ESR and DCR are series resistors). Switching losses come after the run from declared cards, so that dissipation never feeds back into the solution — the electrothermal loop is open by decision, and the Jacobian says so on its face. - No part catalogue. Every device card shipped in this repo is synthetic. A loss or temperature number is exactly as good as the card it came from, and the tool cannot tell you the card is fiction.
- Not a general DAE solver. Capacitor loops, general inductor cutsets and arbitrary
floating-state topologies are refused with a named
TopologyError, not silently regularized with hiddengminor leakage. Narrow, documented zero-current and inductor-star paths are covered; anything else fails closed. - Bounded active set. Diode active-set search is bounded (64 devices, a candidate budget); exhausting the budget is an error carrying the first cause, never a guess.
- Margins attest a declared compensator on an averaged model of one PWM leg in CCM,
not a closed-loop proof: you state the compensator and the tool tells you what it
implies. Closed-loop time-domain runs use the signal-block layer (including a
piblock), which is a separate, simulated thing. - Modal and coverage reports are observations, not verdicts. A run with zero natural commutations means that coverage was not exercised — never that the mechanism passed.
- Out of scope entirely: real-time/HIL, production code generation, FEM/PCB/field simulation, native model files of commercial simulators, bit-for-bit reproduction of another simulator.
Unknown capability is treated as unsupported. If you can get the tool to produce a confident number outside these boundaries, that is a bug worth reporting.
Reproducibility
simulate refuses to overwrite a non-empty directory and writes a portable bundle:
run.json (manifest, hashes, platform), model.resolved.json, waveforms.csv,
events.jsonl, metrics.json, physics-audit.json, mode-coverage.json,
confidence-card.json, warnings.json, summary.json, plus an artifact-manifest.json
with SHA-256 and byte count for every file. Topology- and analysis-specific artifacts
(analytic-checks.json, gate-drive.json, resonance.json, losses-thermal.json,
harmonics.json, modulation-cycles.jsonl, …) appear when the model calls for them.
Lean runs (--lean)
A parameter search runs the same circuit hundreds of times and reads three numbers back;
it does not read the energy balance of run 417. simulate --lean is that run: it skips
the per-component power quadrature and the dense state-extrema search outside the
reported last-half window, which is most of the cost of a run.
It is a reduction of claims, not of accuracy. Everything a lean run still reports —
waveforms, metrics.json, mode-coverage.json, the published state_extrema — comes
out of the identical code path and is bit-for-bit identical to the full run's; the
integration itself is untouched, so the step sequence and the trajectory match exactly.
What is dropped is dropped whole, never approximated: physics-audit.json carries
status: not_computed, and per-interval extrema outside the reported window are null
with state_extrema_method: not_computed_in_lean_evidence_mode.
A lean bundle says so in four places — the console, warnings.json
(EVIDENCE_MODE_LEAN, severity warning), summary.json and run.json
(evidence_mode) — and evidence_mode is written in both modes, so a full bundle
states its scope rather than implying it by silence. Screen with --lean, then re-run
the finalists without it: that is the run you show.
Four hashes bind a result: source, resolved physics model, request and run. Two spellings
of the same circuit share a physics hash; a changed tolerance changes the request hash.
This is what makes "did anything actually change between these two revisions?" a
mechanical question — see pear-sim compare.
Metrics are continuous-time quadratures over topology segments; anything computed on the
requested grid is named sample_* so the two are never confused. Adaptive solvers take
different internal steps across platforms and versions, so comparisons belong on an
explicit output grid with declared tolerances, never on byte equality.
Exit codes: 0 all applicable blocking checks passed · 1 evidence retained with a
blocking fail / not_comparable / not_run · 2 invalid request, input, topology or
destination.
The design-loop layer (in progress)
Beyond running one model, the project is growing the pieces needed to choose a design rather than only check one. These are early and marked experimental:
- Design specification — requirements as a typed object that separates hard constraints from soft ones and asks a question instead of inventing a default.
- Topology library — one YAML file per topology: prerequisites, the switching cycle as data, and a mandatory list of known limitations (an empty list is a claim, so the format refuses it).
- Gradient engine — sensitivities of steady-state metrics to design parameters (duty, dead time, L, C, load, phase shift) computed on the exact period map, with saltation for natural commutation. It gives direction; the oracle remains the only thing that issues a pass.
Direction and open items: component roadmap and the issue tracker.
Working with it
pear-sim --help is the command reference. Beyond the four commands above:
catalog, capabilities, spec, render, evaluate, analyze, compare, sweep, batch, sensitivity,
verify, review, topology, doctor, mcp.
Three surfaces, one contract: the CLI, a typed Python API (pear_sim.api,
docs) and the MCP server (agent interface) — the
same scenario through any of them produces a bit-identical run_sha256.
pear-sim doctor reports versions, package integrity, available oracle backends and a
float-determinism probe — run it first when results differ between two machines.
Reviewing work: review prepare builds a hash-bound packet of what was simulated,
annotate appends hash-chained comments, check verifies integrity and decision
binding. It is an auditable transport format — not a signature authority and not
organizational authorization; reviewer identity is not authenticated.
Development: uv run pytest (about 1,200 tests) and uv run ruff check . must be green
per commit. Contributor rules: CONTRIBUTING.md and
AGENTS.md; architecture in docs/architecture.md.
CI runs Linux (Python 3.11 and 3.13), Windows and macOS.
Implementation notes
The kernel is event-driven and piecewise linear: between two switching events the circuit is a linear time-invariant system assembled by modified nodal analysis, and the events — scheduled gate edges and natural diode commutations — are located as exact instants, not stepped over. That keeps capacitor voltages and inductor currents continuous, makes runs deterministic, and lets steady state, small-signal averaging and sensitivities work on the same exact period map. Details and the reasons behind the main choices: architecture and the ADRs. A compiled core is planned as an optional backend behind the same model format, CLI and MCP contract.
Contributing
Contributions are welcome — please read CONTRIBUTING.md first. In short: every change rests on public mathematics, standards, openly licensed sources or your own work; no proprietary or decompiled code, copied component libraries or demo models, or model files from commercial tools; every bugfix starts with a failing test; and a new capability is registered with honest claim boundaries.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project is licensed under the Apache License 2.0, without any additional terms or conditions.
The product name is PEARSim; the distribution and the executable are pear-sim, the
import package is pear_sim — see ADR 0003.
License
Copyright 2026 PEAR Power and the PEARSim contributors.
Licensed under the Apache License, Version 2.0. The license does not grant permission to use the PEAR Power or PEARSim names or logos except as required to describe the origin of the software. Third-party datasets, examples and optional backends keep their own notices.
Metadata
Release files for pear-sim 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pear_sim-0.1.0.tar.gz | 1.4 MB | Without provenance | Details for pear_sim-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pear_sim-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for pear_sim-0.1.0-py3-none-any.whl |
Total release size: 2.1 MB
Release files / pear_sim-0.1.0.tar.gz
| Download URL | pear_sim-0.1.0.tar.gz |
|---|---|
| Size | 1.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3fd8680941aa2d06b1e0b0dc227397073974fa4356bc3e5070e4fa19f2e81f67
|
|
BLAKE2b-256 checksum How to use checksums |
aa3273b020e01ff05c00ef9331d7f0154eae454773f3fe36b032c84aa047400f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / pear_sim-0.1.0-py3-none-any.whl
| Download URL | pear_sim-0.1.0-py3-none-any.whl |
|---|---|
| Size | 749.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60ca8a4210a0389c0cc2606ec0a9115d2ff98ed89a4f8431509c3e59625e70a5
|
|
BLAKE2b-256 checksum How to use checksums |
596050f511c6e61aeb47595e4e153169dca399f2e07cbe18c3263ccfe23c23af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for pear-sim 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.