Skip to content

razorback16/neospice

Repository files navigation

neospice

CI PyPI Python versions

neospice is an independent C++20 reimplementation of the SPICE circuit simulator. Its solver, analysis flow, and device models derive from UC Berkeley SPICE3 (BSD-licensed) and ngspice -- re-architected around a clean Device interface, native Python bindings, and an embeddable C++ API. MIT licensed; see NOTICE for full third-party attribution.

It reads standard SPICE netlists and produces ngspice-compatible results, runs up to 8.6x faster per analysis in-process on the benchmark suite, and pairs a self-contained Sparse 1.3-compatible solver stack and auto-differentiated behavioral sources with a modern embeddable API for EDA tools, optimization loops, and notebooks.

Features

  • 10 analysis types -- DC OP, DC sweep, transient (adaptive Trap/Gear-2/BE), AC small-signal, noise (adjoint method), transfer function, sensitivity, pole-zero, Fourier/THD, parameter sweep (.step), and .measure post-processing
  • 32 device models -- passives, independent/dependent/behavioral sources, switches, transmission lines, diodes, BJTs, JFETs, MESFETs, HFETs, and MOSFETs through BSIM4v7
  • Embeddable C++ API -- Simulator/Circuit/Result types with handle-based and string-based accessors, typed device methods, and circuit introspection
  • High performance -- NeoSolver (self-contained Sparse 1.3-compatible LU), G/C matrix caching for AC, adjoint-method noise
  • ngspice-compatible -- reads standard SPICE netlists, writes .raw files in ngspice format
  • 972 tests validated against ngspice with tolerances as tight as 1e-6
  • Validated at scale -- runs the full 34,908-model KiCad SPICE library value-matched against ngspice: 93.6% agreement on the 25,843 decks both simulators solve (24,201 match; only 17 where ngspice converges and neospice does not)

Quick Start (C++)

Prerequisites

  • C++20 compiler (GCC 12+ or Clang 15+)
  • CMake 3.20+
  • OpenBLAS
  • SLEEF (vectorized math library)

See docs/building.md for platform-specific install commands.

Build

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)

Run

# Simulate a netlist
./build/neospice circuit.cir

# Specify output file
./build/neospice circuit.cir -o result.raw

# Split output by analysis type
./build/neospice circuit.cir --split

Output is written in ngspice-compatible .raw format, viewable in any waveform viewer (e.g. GTKWave, KST, ngspice's built-in plot).

Test

cd build && ctest -j$(nproc)

Analyses

Analysis Netlist Command Description
DC operating point .op Newton-Raphson with GMIN/source stepping fallback
DC sweep .dc V1 0 5 0.1 1D and nested 2D parameter sweeps
Transient .tran 1n 100n Adaptive timestepping with LTE control
AC small-signal .ac dec 10 1 100meg DEC/OCT/LIN frequency sweeps
Noise .noise v(out) V1 dec 10 1 100meg Adjoint method, per-device breakdown
Transfer function .tf v(out) V1 Gain + input/output impedance
Sensitivity .sens v(out) DC sensitivity to all parameters
Pole-zero .pz Transfer function poles and zeros
Fourier .four 1meg v(out) Harmonic decomposition + THD
Parameter sweep .step param R1 1k 10k 1k Sweep any parameter across analyses

Device Models

Category Models
Passives R (with TC, AC resistance, flicker/thermal noise), C, L, K (mutual inductance)
Sources V, I -- DC, PULSE, SIN, PWL, EXP, SFFM, AM waveforms
Dependent sources E (VCVS), G (VCCS), F (CCCS), H (CCVS) -- linear, POLY, TABLE
Behavioral B-source -- expression-based with auto-diff Jacobians, DDT, IDT, PWL, TABLE
Switches S (voltage-controlled), W (current-controlled) -- hysteresis
Transmission lines T (lossless Branin), O (LTRA lossy -- RC/RG/LC/RLC)
Diode Standard diode (level 1)
BJT Gummel-Poon, VBIC (levels 4/9/12/13)
JFET JFET (Shichman-Hodges), JFET2 (Parker-Skellern)
MESFET MES (GaAs MESFET -- NMF/PMF)
HFET HFET1 (Curtice Cubic), HFET2 (Chalmers)
MOSFET MOS1, MOS3, MOS9, BSIM3v32, BSIM3, BSIM4v7, BSIMSOI, HiSIM2, HiSIM_HV

C++ API

Running a netlist

#include "neospice/neospice.hpp"

neospice::Simulator sim;
auto ckt = sim.load("amplifier.cir");
auto result = sim.run(ckt);

// Typed access to results
auto& ac = std::get<neospice::ACResult>(result.analysis);
auto gain_db = ac.magnitude_db("out");
auto phase = ac.phase_deg("out");

Individual analyses

auto dc = sim.run_dc(ckt);
double vout = dc.voltage("out");

auto tran = sim.run_transient(ckt, 1e-9, 1e-6);
auto vout_wave = tran.voltage("out");   // vector<double>

auto ac = sim.run_ac(ckt, neospice::ACMode::DEC, 10, 1.0, 100e6);
auto gain = ac.magnitude_db("out");     // vector<double>

Building circuits programmatically

using namespace neospice;

Circuit ckt;
auto in  = ckt.node("in");
auto out = ckt.node("out");

ckt.V("V1", in, GND, 0.0, 1.0);  // DC=0, AC=1
ckt.R("R1", in, out, 1e3);
ckt.C("C1", out, GND, 100e-12);

Simulator sim;
auto ac = sim.run_ac(ckt, ACMode::DEC, 10, 1.0, 100e6);
auto gain = ac.magnitude_db("out");

Handle-based result access

// String-based (works with any circuit)
double vout = dc.voltage("out");

// Handle-based (O(1) dense array access, type-safe)
NodeId out_id = ckt.find_node("out");
double vout_h = dc.voltage(out_id);
auto vout_ac  = ac.magnitude_db(out_id);

Measurement utilities

#include "neospice/measure.hpp"

NodeId out_id = ckt.find_node("out");
double bw     = measure::bandwidth_3db(ac, out_id);
double rt     = measure::rise_time(tran, out_id, 0.5, 4.5);
double st     = measure::settling_time(tran, out_id, 5.0, 0.05);
double vrms   = measure::rms(tran, out_id);

Circuit introspection

auto nodes   = ckt.node_names();            // {"in", "out", ...}
auto devices = ckt.device_names();          // {"R1", "C1", "V1"}
auto info    = ckt.device_info("R1");       // type, nodes, value
auto conn    = ckt.devices_at_node("out");  // {"R1", "C1"}

// Handle-based introspection
NodeId nid   = ckt.find_node("out");
DevId  did   = ckt.find_device("R1");
auto   name  = ckt.name(nid);              // "out"
auto   dinfo = ckt.device_info(did);       // DeviceInfo struct

Python

pip install neospice

Prebuilt wheels are published for CPython 3.10–3.14 on Linux (x86_64, aarch64) and macOS Apple Silicon (arm64). On other platforms pip builds from the source distribution, which needs a C++20 compiler plus OpenBLAS and SLEEF.

import neospice as ns
import matplotlib.pyplot as plt

# One-liner convenience functions
dc = ns.dc("amplifier.cir")
print(dc.voltage("out"))

ac = ns.ac("filter.cir", mode="dec", npoints=100, fstart=1, fstop=1e9)
plt.semilogx(ac.frequency, ac.magnitude_db("out"))

tran = ns.transient("osc.cir", tstep=1e-9, tstop=1e-6)
plt.plot(tran.time, tran.voltage("out"))

# Parse inline netlists directly
dc = ns.dc("Divider\nV1 in 0 DC 10\nR1 in mid 1k\nR2 mid 0 1k\n.op\n.end\n")
print(dc.voltage("mid"))  # 5.0

# Or use the full Simulator API
sim = ns.Simulator()
ckt = sim.load("amplifier.cir")          # from file
ckt = sim.parse("...\n.op\n.end\n")      # or from string
result = sim.run_ac(ckt, ns.ACMode.DEC, 100, 1, 1e9)

# Build circuits programmatically with typed methods
ckt = ns.Circuit()
ckt.V("V1", "in", "0", 0.0, 1.0)        # DC=0, AC=1
ckt.R("R1", "in", "out", 1e3)
ckt.C("C1", "out", "0", 100e-12)

# SPICE engineering notation
from neospice import parse_value
r = parse_value("4.7k")                  # 4700.0

All result vectors are returned as NumPy arrays.

Performance

Benchmarked in-process against ngspice-42 on Intel Core Ultra 9 285K, GCC 14, -O3. Both simulators linked as libraries -- no subprocess overhead, no file I/O in timed sections. ngspice uses its default Sparse 1.3 path; .options klu is not used. Median of 30 runs.

Benchmark ngspice neospice Speedup
Parse THS4131 (77 nodes) 435 us 312 us 1.4x
Parse resistor divider 45 us 11 us 4.1x
DC OP THS4131 (14 BJTs) 623 us 483 us 1.3x
DC OP resistor divider 54 us 10 us 5.4x
AC THS4131, 81 points 1.04 ms 706 us 1.5x
AC THS4131, 8001 points 22.35 ms 23.33 ms 1.0x ngspice
AC RC lowpass, 91 points 120 us 18 us 6.7x
Transient RC lowpass, 500 us 1.17 ms 284 us 4.1x
Transient RLC series, 100 us 1.66 ms 397 us 4.2x
Transient pulse source, 100 us 1.02 ms 118 us 8.6x
Noise resistor divider, 91 pts 91 us 54 us 1.7x
DC sweep V1, 1001 pts 847 us 207 us 4.1x
E2E THS4131 (.op + .ac) 746 us 633 us 1.2x
E2E OPA1632 (.op + .ac) 6.72 ms 3.59 ms 1.9x

See docs/performance-comparison-with-ngspice.md for the full methodology and results.

Netlist Compatibility

Beyond core devices and analyses, neospice handles the usual deck directives and syntax:

  • .param expressions with arithmetic, functions (sqrt, log, exp, sin, if, ...)
  • .subckt / .ends with parameter defaults (recursion limit 100)
  • .include / .lib with section selection and circular-inclusion detection
  • .global, .ic, .nodeset, .options, .func, .save
  • SPICE suffixes: T, G, MEG, k, m, u, n, p, f

Project Structure

cli/            Command-line interface
include/
  neospice/     Public API headers (types.hpp, neospice.hpp, measure.hpp)
src/
  api/          C++ API (Simulator, typed device methods, measurement utils)
  core/         Analysis engines and linear solvers
  devices/      32 device models, each self-contained with factory registration
  parser/       Netlist parser and expression evaluator
  output/       Raw file writer
python/
  bindings.cpp  nanobind C++ → Python bridge
  neospice/     Python package (convenience API, SPICE notation parser)
tests/
  unit/         Unit tests for all components (970+)
  devices/      Per-device validation against ngspice
  circuits/     Integration test netlists
  python/       Python binding tests
  bench/        Performance benchmarks
docs/           Architecture, performance, and design documentation
tools/          Device migration tooling (ngspice model auto-porter)

Examples

Comparator Relaxation Oscillator

A TLV3201-based Schmitt trigger oscillator simulated with the Python API. Comparator relaxation oscillator.

Circuit Schematic

Simulation Waveforms

Contributing

See docs/building.md for platform-specific setup instructions, including building libngspice from source on macOS.

Documentation

Roadmap

Phase Feature Status
Handle-based API redesign (NodeId/DevId, typed methods, dense results, measurements) Done
1 Python bindings (nanobind, PyPI wheels, typed construction) Done
2 Parallel parameter sweeps / Monte Carlo Planned
3 WebAssembly build for browser simulation Planned
4 Adjoint sensitivity / gradient computation Planned
5 Incremental re-simulation Planned
6 GPU-accelerated simulation (CUDA) Planned
7 Extended devices (BSIM-CMG, legacy MOS) Ongoing
8 PWL (piecewise-linear) simulation for switching converters Planned
9 Digital event simulation Planned
10 Mixed-signal co-simulation Planned
11 Verilog-A device models (syntax not yet supported) Planned
12 ML-guided DC convergence — learned initial-guess predictor (GNN) to seed Newton Research

See docs/ROADMAP.md for details.

Credits & SPICE lineage

neospice descends from the Berkeley SPICE family. It is an independent C++20 reimplementation, but its solver, analysis flow, and device models are derived — in many places translated — from UC Berkeley SPICE3 and ngspice. We gratefully acknowledge that lineage:

  • L. W. Nagel and D. O. Pederson, "SPICE (Simulation Program with Integrated Circuit Emphasis)," Memorandum No. ERL-M382, UC Berkeley, April 1973.
  • L. W. Nagel, "SPICE2: A Computer Program to Simulate Semiconductor Circuits," Memorandum No. ERL-M520, UC Berkeley, May 1975.
  • SPICE2G6 (1983) — the Fortran reference release.
  • SPICE3F5 (UC Berkeley, 1990s) — the BSD-licensed C rewrite that neospice's core and device code is translated from.
  • ngspice — the maintained SPICE3F5 descendant, used as the reference/ground-truth implementation that neospice is validated against.

The NeoSolver sparse-LU stack derives from Kenneth Kundert's Sparse 1.3 (UC Berkeley), with AMD fill-reducing ordering from Tim Davis's SuiteSparse.

Berkeley SPICE3 is distributed under a permissive BSD-style license ("Copyright Regents of the University of California"), which is what allows neospice to be redistributed under the MIT license. See NOTICE and CREDITS.md for the full attribution.

About

Modern SPICE simulator

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages