Automatic placement¶
Describe the circuit like a SPICE netlist and let mc9 place the parts, route the wires and set the labels.
n = mc9.Netlist()
n.V("V1", "src", "0", sources.sine(3.5e-3, 2224))
n.R("R1", "src", "ue", "200")
n.C("C1", "ue", "b", "3.47u")
n.R("R2", "vcc", "b", "233.2k")
n.Q("Q1", c="col", b="b", e="0", model="BF420")
n.R("R3", "vcc", "col", "1.2k")
n.C("C2", "col", "0", "12n")
n.C("C3", "col", "ua", "553n")
n.R("R4", "ua", "0", "1.2k")
n.VB("V2", "vcc", "0", "18")
c = n.build("ce_stage") # -> mc9.Circuit, drawn and ready
"0" (or gnd) is ground. Every node you name appears as a node label in the schematic, so v(ua) works in curves; names
starting with _ stay unlabelled. Two-terminal parts take their nodes in order (R, C, L: first node on the left;
V, VB, I: plus first; D: anode first); transistors are Q(name, c, b, e, model), op-amps
OPAMP(name, plus, minus, out, vcc, vee, model).
How it places things¶
- Classify nodes: ground, supply rails (DC sources to ground), the input (the first source with
Sin/Pulse/AC) and the output (the node farthest along the signal path that is not an active device's input). - The backbone - shortest signal path from input to output - runs left to right. Transistors are traversed base to collector, op-amps input to output.
- Everything else hangs off backbone nodes: parts to ground go down, parts to a rail go up, series chains are stacked, side pins (emitter, second op-amp input) get their own branches. Parts joining two existing nodes (feedback) become bridges above the circuit.
- Ground and rails are buses; sources sit at the ends. If a drawing would cross wires, that net falls back to named labels (MC9 treats equally named nodes as one net) - always electrically correct, never a crossing.
- The result is checked (
check,audit, connectivity equivalence with your netlist) and re-spaced if boxes or labels would collide.
s = n.schematic(mode="ac") # inspect before writing; mode="ac" leaves room for phasor boxes
print(s.notes) # anything unusual ("U1 could not be placed", ...)
c = s.build("ce_stage", labels="ac")
Options: mode="dc"|"ac", gap= (minimum wire length), input=, output= (override detection), rails_as_labels=True,
ground_labels=True (a ground symbol on every ground pin instead of a bus).
Limits¶
The placer targets the usual teaching circuits: dividers, RC/RL/RLC networks, diode clippers and rectifiers, single transistor
stages (with feedback), op-amp stages. Large or non-planar circuits may fall back to many labels; multi-pin parts inside
branches (a transistor in a shunt path) are connected by name rather than drawn. Use Schematic when you want full control.
Auto-placement never changes the circuit: the tests check that the nets of every generated drawing equal the netlist.
Starter circuits¶
mc9.library has ready-made teaching circuits as parametrised netlists - handy for learning, demos and tests:
from mc9 import library
c = library.voltage_divider(vin=12, r1="2.2k", r2="1k").build("div")
c = library.ce_amplifier(vcc=15, rc="2.2k").build("amp") # common-emitter stage with base bias resistor
c = library.inverting_amplifier(gain=20).build("inv")
library.CATALOG # name -> factory
voltage_divider, rc_lowpass, rc_highpass, rlc_lowpass, diode_clipper, ce_amplifier, ce_feedback_amplifier,
inverting_amplifier, noninverting_amplifier.