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/
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).
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.