Skip to content

Python API

Opening circuits

mc9.open(src, name=None)

Import a host .cir into the sandbox (read-only on the original) and return a Circuit.

mc9.attach(name)

Circuit already in the sandbox (e.g. built with Schematic or imported earlier).

mc9.close_all()

Circuit

mc9.api.Circuit

parts()

{'R1': {'type': 'Resistor', 'RESISTANCE': '200'}, ...}

get(part, attr)

set(part, attr, value)

c.set('R2','RESISTANCE','233.2k'); c.set('V2','VALUE',21.6). Returns the old value.

limits(analysis, **kv)

Read/set raw [Limits] keys of an analysis ('transient','ac','dc','distortion','dynamic_ac','dynamic_dc').

curves(analysis, *exprs)

Replace an analysis' curves. Items: 'v(Ua)', ('F','db(v(Ua))') or a dict:

dict(y="v(out)", x="T", plot=2, xrange=(0, 1e-3), yrange=(-1, 1), ylog=False, xgrid=1e-4, color="#FF0000") (plot groups curves into stacked plots; xrange="auto" autoscales). A frequency-domain curve in a transient analysis is an FFT plot: ("F", "db(mag(fft(v(out))))").

show(**flags)

Schematic display: c.show(voltages=True, currents=False, power=False, condition=False, nodes=False).

run(analysis, timeout=None)

Run an analysis with the settings currently in the file; its plot stays open for cursors()/shot().

op()

DC operating point: node voltages and device tables (IC, gm, power, ...). Fast: no waveform run.

ac(fmin=1, fmax=20000000.0, points=401, curves=None, temp=None, run=True, **limits)

AC sweep (always logarithmic). curves default: keep the file's, e.g. ['db(v(Ua)/v(Ue))','ph(v(Ua)/v(Ue))'].

transient(t_stop, t_start=0, points=1001, curves=None, max_step=None, temp=None, state='Zero', run=True, **limits)

Transient. Numeric table = points rows between t_start and t_stop; steady-state: t_start > 0.

dc(source, start, stop, step, curves=None, temp=None, run=True, **limits)

DC sweep of a source/part: c.dc('V2', 0, 20, 0.5, ['v(5)']). temp may be a list -> one run per temperature.

distortion(f0, source, amplitude, out, cycles=20, max_step=None, temp=25, run=True, **limits)

Harmonic distortion analysis (HARM/IHD/THD curves of out at fundamental f0).

dynamic_dc(temperature=25, slider_percent=1, text=False)

Node voltages/currents drawn on the schematic (like lab-report 'режим спокою' figures).

dynamic_ac(freq, temperature=25, first='Mag', second='PhaseDeg', slider_percent=10, text=False)

Phasors on the schematic. first: Mag|MagDB|Real|None, second: PhaseDeg|PhaseRad|Imag|None.

cursors(left, right=None)

Put MC9's cursors at X values in the open plot (MC9 then shows its own cursor table).

back()

Leave the analysis window (return to the schematic).

shot(file_name, region='plot', trim=True)

Native MC9 screenshot -> sandbox/exchange/. region: plot | schematic | window.

export(dst)

Copy the (edited) circuit to a NEW host path; refuses to overwrite.

close()

Results

mc9.api.Result

Numeric output of one analysis. Behaves like its first run; stepped runs are in .runs.

select(**params)

Stepped runs whose parameters match, e.g. result.select(R1=2000) (numbers compare with 0.1 % tolerance).

mc9.api.Run

One run's data: x = first column. Helper methods interpolate (log-x for wide AC sweeps).

params property

What this stepped run used: {"Temperature": 25.0, "R1": 1000.0} (empty when nothing was stepped).

value_at(col, x)

Interpolated y at x.

cross(col, level, edge=None)

x values where the curve crosses level (interpolated). edge: 'rising' | 'falling' | None.

peak(col, xmin=None, xmax=None)

(x, y) of the maximum within [xmin, xmax].

amplitude(col, xmin=None, xmax=None)

(max - min) / 2 inside the window (sine amplitude in steady state).

mc9.api.OP

DC operating point: node voltages and per-device values (gm, IC, power, ...).

v(node)

Node voltage by name or number (case-insensitive).

Measurements

mc9.measure

Lab-style measurements on top of mc9.Circuit (each helper runs the analyses it needs in MC9).

from mc9 import measure
bw = measure.bandwidth(c, "db(v(Ua)/v(Ue))")                 # G0, f_low, f_high at G0-3 dB
rin = measure.rin(c, "v(Ue)", "i(R1)", f=2224)                # |v/i| at f
rout = measure.rout(c, "v(Ua)", load="R4", f=2224)            # load-removal method
k = measure.thd(c, f0=2224, source="V1", amplitude=35e-3, out="v(Ua)")
r2 = measure.solve(c, "R2", "RESISTANCE", lambda op: op["Q1"]["VCE"], target=9.5, lo=100e3, hi=500e3)

Bandwidth dataclass

bandwidth(c, expr, fmin=1, fmax=20000000.0, points=801, drop_db=3.0, reference=None, cursors=False)

-3 dB band of a gain curve [dB]. reference: take G0 at this frequency instead of the maximum.

gain_at(c, out, inp, f, points=801)

AC gain out/inp at one frequency: {'mag','db','phase_deg'} (interpolated from a log sweep).

rin(c, node_v, current, f, points=801)

Input resistance |Z| = |v(node)/i(part)| at f, from an AC run (current = e.g. 'i(R1)').

rout(c, out, load, f, attr='RESISTANCE', open_value='1T', points=801)

Output resistance by load removal: Rout = (U_open / U_loaded - 1) * RL. Restores the load afterwards.

thd(c, f0, source, amplitude, out, periods=10, settle_periods=80, samples_per_period=128, n_harm=10)

Harmonic distortion of node/expression out for a sine source of given amplitude (steady state). Runs a transient, DFTs whole periods. Returns {'h': {n: amplitude}, 'k_pct': {n: % of fundamental}, 'thd_pct'}. Accuracy is limited by MC9's 6-digit numeric output (fine for THD down to ~0.01 %).

solve(c, part, attr, f, target, lo, hi, tol=0.001, max_iter=14, op=True)

Find the value of part.attr in [lo, hi] for which f(measurement) == target (bisection, monotonic f). f receives the OP (operating point); each iteration costs one MC9 start (~25 s).

set_sine(c, source, amplitude=None, freq=None, attr='VALUE')

Edit the 'Sin offset amp freq ...' part of a source's VALUE text. Returns the old text.

dft_harmonics(t, y, f0, n_harm=10)

Amplitudes of harmonics 1..n of f0 from uniformly sampled y over a whole number of periods.

Sources

mc9.sources

VALUE strings for MC9's SPICE-style 'Voltage Source' / 'Current Source' parts.

s.add("V", "V1", sources.sine(0.01, 1e3, ac=0.01)) # -> 'DC 0 AC 0.01 0 Sin 0 0.01 1000 0 0 0' s.add("V", "V1", sources.pulse(-1, 1, 1e-3, rise=1e-9, fall=1e-9))

sine(amplitude, freq, offset=0, ac=None, dc_level=0, delay=0, damping=0, phase=0)

Sine source; ac = AC small-signal magnitude (needed for AC analysis; default = amplitude).

pulse(v1, v2, period, width=None, delay=0, rise=1e-09, fall=1e-09, ac=None)

Pulse/square source switching v1 -> v2. width defaults to half the period (50 % duty).

Schematic builder

mc9.schematic.Schematic

add(kind, name, value=None, at=(0, 0), rot=None, **extra)

Place a component. kind: 'R','C','L','V','VB','I','D','Q','QP','GND','OPAMP','VSIN','VPULSE','M' or an MC9 name. extra: more attributes, e.g. MODEL='BF420' for transistors/diodes.

wire(a, b, via='h')

Wire two points (grid tuples or Part pins). Not axis-aligned -> L-shaped (via='h': horizontal first).

node(name, at)

Name a node (shows as a label; use it in expressions: v(Ue)).

nets()

point -> net id, as MC9 sees it: wire endpoints are joined, nodes with the same label are one net, and all Ground symbols are node 0.

pin_nets()

{(part name, pin): net id} as MC9 will see it (see nets). Use it to verify a drawing against a netlist.

check()

Static problems: unconnected pins, wire crossings that don't share an endpoint.

auto_labels(mode='dc', chars=None)

Choose PART/VALUE label offsets so they avoid bodies, wires, voltage boxes and each other. mode: 'dc' (short boxes), 'ac' (wide phasor boxes), 'none' (ignore voltage boxes).

audit(mode='dc', chars=None)

Predict visual collisions in a dynamic-analysis screenshot (mode 'dc' or 'ac'). Voltage boxes are drawn by MC9, so overlaps with them mean: space the components further apart.

bbox()

Grid-unit bounding box of everything drawn (bodies, wires, labels, node names, voltage boxes).

to_cir(zoom=None, margin=24)

The .cir text. The drawing is shifted to non-negative coordinates and MC9's scroll/zoom ([Page] SB 3 / Scale 3) are set so the whole drawing is centered in the canvas (zoom<=2: auto-fit).

build(name, labels='dc', chars=None)

Auto-place labels, write the circuit into the sandbox and return an mc9.Circuit. chars: expected width of MC9's voltage boxes in characters (default 8 for dc, 17 for ac).

mc9.schematic.Part dataclass

body()

Absolute body rectangle in design px.

Netlist and automatic placement

mc9.netlist.Netlist

add(kind, name, nodes, value=None, **extra)

Any MC9 part by name. nodes: a list in pin order, or a dict {"Drain": "d", "Gate": "g", ...}.

Parts that are not in the built-in set (JFETs, PMOS, Zener, transformers, controlled sources, switches, logic, macros ...) are fetched from MC9 itself the first time they are used (see mc9.catalog.learn).

build(name, labels='dc', chars=None, **options)

Place + route + write the circuit; returns an mc9.Circuit.

labels / chars as in Schematic.build (room for MC9's voltage boxes); other options as in autoplace.place (mode, gap, input, output, rails_as_labels, ground_labels).

schematic(**options)

The placed Schematic (inspect/adjust before .build()).

mc9.autoplace.place(nl, mode='dc', gap=None, input=None, output=None, rails_as_labels=False, ground_labels=False, max_tries=8)

Place and route a netlist. Returns a Schematic (call .build(name)); .notes lists anything unusual.

Starter circuits

mc9.library

Ready-made teaching circuits as Netlists. Parameters are plain values or MC9 strings ("4.7k").

import mc9 from mc9 import library c = library.ce_amplifier(vcc=15).build("amp")

voltage_divider(vin=10, r1='1k', r2='2k')

DC divider: nodes in, out.

rc_lowpass(r='1k', c='159.155n', amplitude=1, freq=1000.0)

First-order RC low-pass (fc = 1/2piRC = 1 kHz by default): nodes in, out.

rc_highpass(c='159.155n', r='1k', amplitude=1, freq=1000.0)

First-order RC high-pass: nodes in, out.

rlc_lowpass(r='50', l='10m', c='1u', rl='1k', amplitude=1, freq=1000.0)

Series R-L into a C || load: second-order low-pass. Nodes in, a, out.

diode_clipper(amplitude=5, freq=1000.0, r='1k', model='1N4148')

Two-sided diode limiter (antiparallel diodes to ground): nodes in, out.

ce_amplifier(vcc=18, rb='233.2k', rc='1.2k', rl='1.2k', rs='200', c1='3.47u', c2='12n', c3='553n', model='BF420', amplitude=0.0035, freq=2224)

RC-coupled common-emitter stage with a base bias resistor. Nodes: src, ue (stage input), b, col, ua (output), vcc.

ce_feedback_amplifier(vcc=18, rb1='62.5k', rb2='3.12k', rc='1.2k', rl='1.2k', rg='100', cb='396n', cc='737n', ch='110p', model='BF420', amplitude=0.01, freq=1000)

Common-emitter stage with parallel voltage feedback (RB1 collector->base, CH across it). Nodes: src, ue, x, b, col, ua, vcc.

inverting_amplifier(gain=10, r1='10k', rl='10k', supply=15, model='LM741', amplitude=0.1, freq=1000.0)

Op-amp inverting stage, gain -gain. Nodes: in, inv, out, vcc, vee.

noninverting_amplifier(gain=10, rg='1k', rl='10k', supply=15, model='LM741', amplitude=0.1, freq=1000.0)

Op-amp non-inverting stage, gain 1+Rf/Rg. Nodes: in, fb, out, vcc, vee.

Configuration

mc9.config.Config dataclass