Skip to main content

PyAMS

Python library for analog and mixed-signal simulation (PyAMS Library)

PyAMS-lib


PyAMS-lib Docs V 0.3.0 Python 3+ PyPy 3+ MIT License

Transient analysis AC analysis DC sweep

PyAMS Library

What is pyams-lib?

pyams_lib is a Python package designed to simplify the modeling of analog and digital elements and the simulation of electronic circuits. It provides:

  • The ability to create custom models of electrical components.
  • Simulation of circuits in different modes of operation (DC, DC sweep, transient, AC).
  • Visualization of simulation results using matplotlib.
  • Compatibility with Python 3+, working across Linux, Windows, and macOS.
  • PyAMS library (pyams_lib) documentation https://pyams-lib.readthedocs.io/.
  • For circuit design using the CAD system, visit the software section at https://pyams.sf.net.

Installation

pip install pyams_lib

To upgrade to the latest version:

pip install --upgrade pyams_lib

Requires Python 3 with numpy and matplotlib.


Contents

  1. Quick start
  2. Modeling
  3. Analysis
  4. Showing the results
  5. Built-in models
  6. More examples

1. Quick start

A half-wave rectifier: a 10 V / 2 Hz sine source, a diode and a 1 kΩ load.

from pyams.lib import circuit
from pyams.models import Resistor, SinVoltage, Diode

# 1. create the elements; the arguments are node names ("0" is ground)
V1 = SinVoltage("In", "0")
D1 = Diode("In", "Out")
R1 = Resistor("Out", "0")

# 2. set their parameters
V1.setParams("Va=10V Fr=2Hz")
R1.setParams("R=1KΩ")

# 3. build the circuit and choose what to record
cir = circuit()
cir.addElements({"V1": V1, "D1": D1, "R1": R1})
cir.setOutPuts("In", "Out")

# 4. choose an analysis, run it, plot
cir.analysis(mode="tran", start=0, stop=2, step=0.001)
cir.run()
cir.plot()

The five steps — elements → parameters → circuit → analysis → results — are the same for every circuit.


2. Modeling

2.1 Building a circuit

An element is created with the names of the nodes its terminals are connected to. Two elements that use the same node name are connected there. The names "0", "gnd" and "ground" all mean the reference node.

R1 = Resistor("In", "N1")      # between node In and node N1
C1 = Capacitor("N1", "0")      # between node N1 and ground

Elements are then added to a circuit as a dictionary {name: element}. The element name is used later to select parameters and signals (for example "R1.R" or "D1.I"):

cir = circuit()
cir.addElements({"V1": V1, "R1": R1, "C1": C1})

cir.setOutPuts(...) selects what is recorded and plotted. It accepts

Output name Meaning
"Out" voltage of node Out
"R1.I" a signal of an element: Element.signal (current)
"R1.V" voltage across the element

If setOutPuts is not called, all node voltages are recorded.

2.2 Setting parameters

Parameters are set with a string of name=value pairs. Values accept engineering prefixes (T G Meg k m u n p f) and optional units:

R1.setParams("R=4.7KΩ")
V1.setParams("Va=5V Fr=50Hz")
C1.setParams("C=100nF")
L1.setParams(L=10e-3)                 # keyword arguments also work
D1.setParams({"Iss": 1e-12, "n": 1.5})  # ... and so does a dict

element.getParams() returns every parameter with its unit and description; an unknown parameter name raises an error that lists the valid ones.

2.3 Writing your own model

A model is a class derived from model.

  • __init__ receives the terminals (node names), declares the signals and the parameters.
  • analog() holds the equations.
from math import exp
from pyams.lib import model, signal, param, voltage, current

class MyDiode(model):
    def __init__(self, p, n):
        self.V = signal('in',  voltage, p, n)     # read:  V = v(p) - v(n)
        self.I = signal('out', current, p, n)     # write: current p -> n
        self.Is = param(1e-14,   'A', 'Saturation current')
        self.Vt = param(0.02585, 'V', 'Thermal voltage')

    def analog(self):
        self.I += self.Is * (exp(self.V / self.Vt) - 1)

Use it exactly like a built-in element:

V1 = DCVoltage("In", "0");  V1.setParams("Vdc=5V")
R1 = Resistor("In", "A");   R1.setParams("R=1KΩ")
D1 = MyDiode("A", "0")

cir = circuit()
cir.addElements({"V1": V1, "R1": R1, "D1": D1})
cir.setOutPuts("A", "D1.I")
cir.analysis(mode="dc")
print(cir.run())      # {'time': [0.], 'A': [0.692...], 'D1.I': [0.0043...]}

Signals — signal(direction, kind, p, n)

Declaration Meaning
signal('in', voltage, p, n) the value of v(p) - v(n); read it inside analog()
signal('out', current, p, n) sig += x adds a current x flowing from p to n through the element
signal('out', voltage, p, n) sig += x imposes v(p) - v(n) = x (an ideal voltage source)
signal('in', current, p, n) the current of the ('out', voltage) signal on the same two nodes (current-controlled elements)

Time derivatives. Inside analog(), ddt(x) is the time derivative of x (it is 0 in DC), and idt(x) its integral. Signals, parameters and time behave like ordinary floats, so math.exp, math.sin, … can be applied directly. A capacitor, for instance, is just:

from pyams.lib import ddt

class MyCap(model):
    def __init__(self, p, n):
        self.V = signal('in',  voltage, p, n)
        self.I = signal('out', current, p, n)
        self.C = param(1e-6, 'F', 'Capacitance')

    def analog(self):
        self.I += self.C * ddt(self.V)

You only write the equations — the solver builds and linearises the system (modified nodal analysis with Newton–Raphson; the Jacobian is obtained by finite differences of analog()), so no derivatives have to be coded by hand.


3. Analysis

The analysis is chosen with cir.analysis(mode=...) and executed with cir.run().

mode Use it for Main arguments
"dc" operating point –
"dc" + param= DC sweep (I–V curves, transfer curves) param, start, stop, step
"tran" behaviour in time start, stop, step (seconds)
"ac" small-signal frequency response start, stop (Hz), step (points / decade)

3.1 DC operating point

cir.analysis(mode="dc")
res = cir.run()
print(res["A"][0], res["D1.I"][0])

3.2 DC sweep

Sweep one parameter ("Element.Parameter") and solve the operating point at every value:

cir.analysis(mode="dc", param="V1.Vdc", start=0, stop=5, step=0.05)
res = cir.run()
# res["sweep"] -> swept values, res["D1.I"] -> diode current
cir.plot()

3.3 Transient analysis

cir.analysis(mode="tran", start=0, stop=0.06, step=1e-4)
res = cir.run()           # {'time': ..., 'In': ..., 'Out': ...}

Options for demanding circuits (switching, stiff, oscillators):

Option Effect
order=2 second-order (BDF2) integration; default 1 = backward Euler
adaptive=True the solver chooses its own step (tiny at fast edges, large elsewhere); step then only sets the spacing of the output points
lte, lte_abs relative / absolute local-error tolerance of the adaptive step
max_step largest internal step
reltol Newton convergence tolerance (default 1e-6)
uic=True start from an all-zero state instead of the DC operating point
progress=True show a progress bar
cir.analysis(mode="tran", start=0, stop=0.006, step=1e-6, adaptive=True, order=2)

3.4 AC (small-signal) analysis

PyAMS solves the DC operating point, linearises every element around it, and solves the complex system at each frequency. The stimulus comes from the sources that define an AC value (ACVoltage, ACCurrent). With a 1 V stimulus, the node phasor is the transfer function.

import numpy as np
from pyams.lib import circuit
from pyams.models import ACVoltage, Resistor, Capacitor

V1 = ACVoltage("In", "0")
R1 = Resistor("In", "Out");  R1.setParams("R=1KΩ")
C1 = Capacitor("Out", "0");  C1.setParams("C=1uF")

cir = circuit()
cir.addElements({"V1": V1, "R1": R1, "C1": C1})
cir.setOutPuts("Out")
cir.analysis(mode="ac", start=1, stop=1e5, step=20)    # 20 points per decade
res = cir.run()

f, H = res["freq"], res["Out"]                          # H is complex
gain_db  = 20 * np.log10(np.abs(H))
phase_deg = np.angle(H, deg=True)
fc = f[np.argmin(np.abs(gain_db + 3.01))]               # -3 dB corner: ~159 Hz
cir.plot()                                              # Bode magnitude + phase

scale="dec" (default) / "oct" give step points per decade / octave; scale="lin" gives step points in total.

3.5 Parametric sweep

cir.sweep(param, list) repeats the whole analysis (transient, DC or AC) for each value of a parameter. run() then returns a list of result dictionaries (each with the extra key "param"), and plot() overlays them.

cir.analysis(mode="ac", start=1, stop=1e5, step=20)
cir.sweep(param="R1.R", list=[100, 1000, 10000])
runs = cir.run()                  # one result per resistor value
cir.plot()

cir.sweep()                       # switch the parametric sweep off

4. Showing the results

Built-in plot

cir.plot()                                   # show a window
cir.plot("result.png", show=False)           # save to a file

plot() adapts to the analysis: a transient run gives one panel per unit (voltages, currents) against time, a DC sweep is plotted against the swept parameter, an AC run gives magnitude and phase against frequency, and a parametric sweep overlays all runs.

Transient (series RLC, rlc.py) AC (series RLC band-pass, ac_analysis.py)
Transient AC

Your own plots and post-processing

run() returns plain NumPy arrays in a dictionary, so you can compute anything with them:

import matplotlib.pyplot as plt

res = cir.run()
t, vout = res["time"], res["Out"]

print("peak output: %.3f V" % vout.max())
print("average    : %.3f V" % vout.mean())

fig, ax = plt.subplots()
ax.plot(t * 1e3, vout, label="Out")
ax.set_xlabel("Time (ms)")
ax.set_ylabel("Voltage (V)")
ax.legend()
fig.savefig("out.png", dpi=150)
Analysis Keys of the result dictionary
tran "time", then one array per output
dc (point) "time" (one value), then one value per output
dc + param "sweep", "Element.Parameter", then one array per output
ac "freq", then one complex array per output
with sweep() a list of the dictionaries above, each with the extra key "param"

5. Built-in models

from pyams.models import Resistor, Capacitor, Diode, BJT   # ... see the list below
Group Models
Passive Resistor, Conductor, Capacitor, CapacitorIc, Inductor, InductorIc, NonlinearResistance, Transformer, TransmissionLine
Independent sources DCVoltage, SinVoltage, ACVoltage, ExpVoltage, PWLVoltage, TrapezoidVoltage, DCCurrent, SinCurrent, ACCurrent, ExpCurrent, PWLCurrent, TrapezoidCurrent
Controlled sources VCVS, VCCS, CCVS, CCCS
Diodes / switching Diode, ZenerDiode, TunnelDiode, Diac, Thyristor, UJT
Transistors BJT, MOSFET, MOSFET4, JFET, IGBT
Analog ICs OpAmp, Comparator, Timer555

Each class documents its terminals, signals and parameters in its docstring:

help(BJT)
print(BJT("c", "b", "e").getParams())

6. More examples

The examples/ folder contains ready-to-run scripts, each saving a figure:

Script What it shows
bridge_rectifier.py, zener_regulator.py rectification and regulation
rlc.py, charge_discharge.py transient response of RC / RLC circuits
ac_analysis.py, elliptic_filter.py AC analysis: RLC band-pass, common-emitter amplifier, op-amp, filters
jfet_curves.py, thyristor_iv.py, ujt_oscillator.py DC sweeps and device characteristics
bjt_astable.py, timer555_astable.py, ring_oscillator.py oscillators and multivibrators
schmitt_opamp.py, comparator_demo.py comparators and Schmitt triggers
chua_opamp.py, van_der_pol.py non-linear oscillators / chaos
transmission_line.py, thyristor_lamp_dimmer.py, igbt_chopper.py power and transmission-line circuits
555 astable JFET output curves Chua double scroll
555 JFET Chua

License

pyams_lib is free to use and distributed under the MIT license.

Metadata

Release files for pyams-lib 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyams-lib 0.3.0
File Size Uploaded Provenance Actions
pyams_lib-0.3.0.tar.gz 64.5 kB Without provenance Details for pyams_lib-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pyams-lib 0.3.0
File Interpreter ABI Platform Actions
pyams_lib-0.3.0-py3-none-any.whl Python 3 none any Details for pyams_lib-0.3.0-py3-none-any.whl

Total release size: 141.6 kB

Release files / pyams_lib-0.3.0.tar.gz

Download URL pyams_lib-0.3.0.tar.gz
Size 64.5 kB
Tags Source
SHA-256 checksum
How to use checksums
151c145ad1ff94e9d9bb23b3b2798cb5c0b2bd3e5c700f3a148f510bd3163ccc
BLAKE2b-256 checksum
How to use checksums
f0863e7bce66ad9cb2c97de2fa542f37f91f499c07ffa41f1c316aeaa76fb703
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Release files / pyams_lib-0.3.0-py3-none-any.whl

Download URL pyams_lib-0.3.0-py3-none-any.whl
Size 77.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c609255989b9a5ff55b139f01e046b8c6c46c6aaebd203cccd23311653e6c5c4
BLAKE2b-256 checksum
How to use checksums
c326a878521ae36fdeb0135dc6479e925f226ef4f5f1f6ad54cce4cdcca016bb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Security for pyams-lib 0.3.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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page