sycan.mna¶
MNA infrastructure: component base, stamping context, and solvers.
Two analysis modes are supported:
"dc"— steady-state operating point. Inductors short, capacitors open. Components with astamp_nonlinearhook (e.g. MOSFETs in sub-threshold) contribute transcendental terms and the solver falls back tocas.solve."ac"— small-signal frequency-domain analysis using a Laplace variables. Capacitors stamp admittancesC; inductors stamp1/(sL). Source AC values override DC values.
Functions
|
Assemble the linear symbolic MNA system |
|
Assemble the full residual vector |
|
Unified DC / AC entry point with assumption support. |
|
Solve the small-signal AC response in the Laplace domain. |
|
Solve the DC operating point symbolically. |
|
Sweep one symbolic parameter across |
|
Small-signal impedance looking into a named |
|
Output-voltage noise PSD at |
|
Pole-zero analysis of a transfer function in the Laplace domain. |
|
Symbolic small-signal sensitivity |
|
Symbolic small-signal transfer function and summary metrics. |
Classes
Netlist element that can stamp itself into an MNA matrix. |
|
|
Symbolic small-signal noise current source. |
|
Result of a pole-zero analysis. |
|
Mutable state handed to each component's |
- class sycan.mna.NoiseSource(name, kind, n_plus, n_minus, psd)[source]¶
Bases:
objectSymbolic small-signal noise current source.
A unit current with one-sided power spectral density
psd(units: A²/Hz) flowing fromn_pluston_minusinternally (same convention asCurrentSource).kindis one of"thermal","shot","flicker";nameis a unique string of the form"<component>.<kind>[.<tag>]"so that callers can dis-aggregate the contributions returned bysolve_noise().- Parameters:
name (str)
kind (str)
n_plus (str)
n_minus (str)
psd (Expr)
- name: str¶
- kind: str¶
- n_plus: str¶
- n_minus: str¶
- psd: Expr¶
- class sycan.mna.StampContext(A, b, node_rows, aux_rows, mode='dc', s=None, x=None, residuals=None)[source]¶
Bases:
objectMutable state handed to each component’s
stampmethod.- Parameters:
A (MutableDenseMatrix)
b (MutableDenseMatrix)
node_rows (dict[str, int])
aux_rows (dict[str, int])
mode (str)
s (Expr | None)
x (MutableDenseMatrix | None)
residuals (list | None)
- A: MutableDenseMatrix¶
- b: MutableDenseMatrix¶
- node_rows: dict[str, int]¶
- aux_rows: dict[str, int]¶
- mode: str = 'dc'¶
- s: Expr | None = None¶
- x: MutableDenseMatrix | None = None¶
- residuals: list | None = None¶
- class sycan.mna.Component[source]¶
Bases:
ABCNetlist element that can stamp itself into an MNA matrix.
Subclasses that introduce a branch-current unknown set
has_aux = True. Subclasses that add transcendental current contributions (e.g. MOSFETs) sethas_nonlinear = Trueand implementstamp_nonlinear`(), which is called during the DC residual pass.Concrete subclasses declare which of their dataclass fields name circuit nodes via
ports— a class variable listing attribute names.Circuitwalks this list to register a component’s nodes without hard-coding per-component attribute names.Every concrete subclass is auto-registered into
Component._registrywhen its module is imported, keyed by class name. The registry is queryable viaComponent.available().- name: str¶
- ports: ClassVar[tuple[str, ...]] = ()¶
- has_aux: ClassVar[bool] = False¶
- has_nonlinear: ClassVar[bool] = False¶
- SUPPORTED_NOISE: ClassVar[frozenset[str]] = frozenset({})¶
- classmethod available()[source]¶
Return the registry of every concrete Component subclass.
- Return type:
dict[str, type[Component]]
- iter_node_names()[source]¶
Yield each circuit node name referenced by this component.
- Return type:
Iterator[str]
- aux_count(mode)[source]¶
Number of auxiliary branch currents the component needs in
mode.- Parameters:
mode (str)
- Return type:
int
- noise_sources()[source]¶
Return the small-signal noise sources this component emits.
Default is no-op; concrete components override to emit thermal / shot / flicker contributions weighted by the
include_noiseselection.- Return type:
list[NoiseSource]
- abstractmethod stamp(ctx)[source]¶
- Parameters:
ctx (StampContext)
- Return type:
None
- stamp_nonlinear(ctx)[source]¶
Add transcendental terms to
ctx.residualsat solve time.Only invoked for DC analysis when the circuit contains at least one component with
has_nonlinear = True. Default is no-op.- Parameters:
ctx (StampContext)
- Return type:
None
- sycan.mna.build_mna(circuit, mode='dc', s=None)[source]¶
Assemble the linear symbolic MNA system
A * x = bformode.- Parameters:
circuit (Circuit)
mode (str)
s (Optional[cas.Expr])
- Return type:
tuple[cas.Matrix, cas.Matrix, cas.Matrix]
- sycan.mna.build_residuals(circuit, mode='dc', s=None)[source]¶
Assemble the full residual vector
A*x - b + nonlinear(x).Useful as a starting point for custom nonlinear solvers (numerical Newton, continuation, noise sampling, etc.) that want the raw symbolic equations rather than the dict form produced by
solve_dc().- Parameters:
circuit (Circuit)
mode (str)
s (Optional[cas.Expr])
- Return type:
tuple[cas.Matrix, list[cas.Expr]]
- sycan.mna.solve_dc(circuit, simplify=True, *, assume=None)[source]¶
Solve the DC operating point symbolically.
If any component reports
has_nonlinear, the solver buildsresiduals = A·x − bplus nonlinear contributions and callssympy.solve(). Otherwise, LU on the linear system.assumeis an optional list ofAssumptionobjects applied to the solution dict after the solve (in addition to any attached to the circuit viaassume()).- Parameters:
circuit (Circuit)
simplify (bool)
assume (Optional[list])
- Return type:
dict[cas.Symbol, cas.Expr]
- sycan.mna.solve_impedance(circuit, port_name, termination='auto', s=None, simplify=False)[source]¶
Small-signal impedance looking into a named
Port.A unit AC voltage is applied across the target port and the resulting branch current is read out;
Z = dv/difollows. Other ports in the netlist are terminated pertermination:"z"— all other ports open (Z-parameter convention)."y"— all other ports shorted (Y-parameter convention)."auto"— other input ports shorted, output ports opened (the usual “sources zeroed, loads open” convention for amplifier input / output impedance).
The test source and any termination wires are added to a throw-away copy of the circuit, so the caller’s circuit is left untouched.
- Parameters:
circuit (Circuit)
port_name (str)
termination (str)
s (Optional[cas.Expr])
simplify (bool)
- Return type:
cas.Expr
- sycan.mna.solve_ac(circuit, s=None, simplify=False, *, assume=None)[source]¶
Solve the small-signal AC response in the Laplace domain.
Nonlinear components (e.g. MOSFETs) contribute no small-signal model yet and are treated as zero-current elements.
assumeis an optional list ofAssumptionobjects applied to the solution after the matrix solve (in addition to any attached to the circuit viaassume()).- Parameters:
circuit (Circuit)
s (Optional[cas.Expr])
simplify (bool)
assume (Optional[list])
- Return type:
dict[cas.Symbol, cas.Expr]
- sycan.mna.solve(circuit, *, mode='dc', s=None, simplify=False, assume=None)[source]¶
Unified DC / AC entry point with assumption support.
mode="ac"returns the Laplace-domain small-signal solution (delegating tosolve_ac()).mode="dc"collapses the s-domain solution ats=0for purely-linear circuits — this is the formal “DC is AC with ω → 0” unification. Circuits containing nonlinear devices fall back tosolve_dc()(the Newton-Raphson / closed-form nonlinear path) because their stamps don’t carry an LTI small-signal model that can be evaluated ats=0.Assumptions attached to the circuit (via
assume()) and any passed inassumeare applied to the resulting solution dict in order. For region-style assumptions, usecheck_assumptions()separately on the returned solution to verify the claim.- Parameters:
circuit (Circuit)
mode (str)
s (Optional[cas.Expr])
simplify (bool)
assume (Optional[list])
- Return type:
dict[cas.Symbol, cas.Expr]
- sycan.mna.solve_noise(circuit, output_node, s=None, simplify=False)[source]¶
Output-voltage noise PSD at
output_nodein the Laplace domain.Walks every component, asks for its
Component.noise_sources(), and superposes their contributions:S_V_out(s) = Σ_k H_k(s) · H_k(-s) · S_k
where
H_k(s) = V(output_node) / I_k(s)is the trans-impedance from the unit-current k-th noise source to the output, andS_k(s)is the source’s spectral density. To turn the symbolic result into a frequency-domain PSD, substitutes = cas.I * omega.Independent V/I sources elsewhere in the circuit are not zeroed explicitly — their small-signal contribution does not enter the transfer-function matrix
A, only the right-hand side, and we rebuildbper noise source so they fall away naturally.Returns
(total_psd, per_source_psd)whereper_source_psdmaps eachNoiseSource.nameto its individual contribution.- Parameters:
circuit (Circuit)
output_node (str)
s (Optional[cas.Expr])
simplify (bool)
- Return type:
tuple[cas.Expr, dict[str, cas.Expr]]
- class sycan.mna.PZResult(H, poles, zeros, numerator, denominator)[source]¶
Bases:
objectResult of a pole-zero analysis.
- Parameters:
H (Expr)
poles (list[Expr])
zeros (list[Expr])
numerator (Expr)
denominator (Expr)
- H¶
Transfer function
H(s) = V(out) / V(in)in the Laplace domain.- Type:
sympy.core.expr.Expr
- poles¶
List of pole locations (roots of the denominator of
H(s)). Each pole is an expression in component parameters ands.- Type:
list[sympy.core.expr.Expr]
- zeros¶
List of zero locations (roots of the numerator of
H(s)).- Type:
list[sympy.core.expr.Expr]
- numerator¶
The raw numerator polynomial / expression of
H(s).- Type:
sympy.core.expr.Expr
- denominator¶
The raw denominator polynomial / expression of
H(s).- Type:
sympy.core.expr.Expr
- H: Expr¶
- poles: list[Expr]¶
- zeros: list[Expr]¶
- numerator: Expr¶
- denominator: Expr¶
- sycan.mna.solve_pz(circuit, output_node, input_source=None, s=None, simplify=False)[source]¶
Pole-zero analysis of a transfer function in the Laplace domain.
Builds the AC MNA system, extracts the transfer function
H(s) = V(output_node) / source_value, then factors it to find poles (roots of denominator) and zeros (roots of numerator).If
input_sourceisNone(default), the system poles are extracted from the source-to-output transfer function using the first AC source found in the circuit. If no AC source is present, aValueErroris raised.- Parameters:
circuit (Circuit) – The circuit under analysis.
output_node (str) – Name of the node whose voltage constitutes the output.
input_source (Optional[str]) – Name of the independent source that drives the input. If
None, the first source with a non-zeroac_valueis used.s (Optional[cas.Expr]) – Laplace variable. If
None,cas.Symbol("s")is created.simplify (bool) – If
True, simplify the transfer function and pole/zero expressions.
- Returns:
Dataclass holding the transfer function, poles, zeros, and raw numerator / denominator.
- Return type:
- sycan.mna.solve_dc_sweep(circuit, parameter, values, simplify=False)[source]¶
Sweep one symbolic parameter across
valuesand return per-point DC.Solves the symbolic DC operating point once (with
simplifydeferred to the caller — keep itFalseto amortise the symbolic cost) and substitutesparameterwith each value invalues. This is the closed-form analogue of SPICE’s.DC vsweepand is useful for plotting transfer characteristics, bias curves, or swept-supply gain.- Parameters:
circuit (Circuit) – Circuit under analysis.
parameter (Union[str, cas.Symbol]) – Symbol (or its string name) to sweep. Must appear in the symbolic DC solution.
values (list) – Iterable of values (numeric or symbolic) to substitute.
simplify (bool) – If
True, simplify the substituted expressions per step.
- Returns:
One dict per swept value, each with the same shape as
solve_dc()’s return value but withparameterreplaced.- Return type:
list[dict[Symbol, Expr]]
- sycan.mna.solve_tf(circuit, output_node, input_source=None, s=None, simplify=False)[source]¶
Symbolic small-signal transfer function and summary metrics.
Returns a dict with the closed-form Laplace-domain transfer function
H(s) = V(output_node) / source_valueplus convenient summary metrics extracted symbolically:H— fullH(s)expressiondc_gain—H(0)(i.e.H.subs(s, 0))hf_gain—lim s→∞ H(s)(viacas.limit)numerator— numerator polynomial ofH(s)(s-domain)denominator— denominator polynomial ofH(s)(s-domain)
Source selection follows the same rule as
solve_pz(): ifinput_sourceisNone, the first source with a non-zeroac_valueis used.- Parameters:
circuit (Circuit)
output_node (str)
input_source (Optional[str])
s (Optional[cas.Expr])
simplify (bool)
- Return type:
dict[str, cas.Expr]
- sycan.mna.solve_sensitivity(circuit, output, parameters=None, mode='dc', s=None, normalized=False, simplify=False)[source]¶
Symbolic small-signal sensitivity
∂V(out)/∂pper parameter.Solves the requested analysis (
"dc"or"ac") symbolically, extracts the output expression foroutput(a node name or a symbol such asSymbol("V(out)")), and differentiates w.r.t. each parameter.- Parameters:
circuit (Circuit) – Circuit under analysis.
output (Union[str, cas.Symbol]) – Node name (string) or unknown symbol (e.g.
Symbol("I(V1)")).parameters (Optional[list]) – List of symbols (or names) to differentiate over.
Nonemeans every free symbol of the output expression that is not an unknown of the MNA system.mode (str) –
"dc"(default) or"ac".s (Optional[cas.Expr]) – Laplace variable for AC mode.
normalized (bool) – If
True, return the relative sensitivity(p / V_out) · ∂V_out/∂p, which is dimensionless and matches the SPICE.SENSconvention for “% per %”.simplify (bool) – Apply
cas.simplify()to each sensitivity expression.
- Returns:
Mapping of each parameter symbol to its sensitivity expression.
- Return type:
dict[Symbol, Expr]