Skip to main content

MnaSystem

Struct MnaSystem 

pub struct MnaSystem {
    pub num_nodes: usize,
    pub num_vsources: usize,
    pub g_matrix: Vec<f64>,
    pub b_matrix: Vec<f64>,
    pub c_matrix: Vec<f64>,
    pub d_matrix: Vec<f64>,
}
Expand description

MNA (Modified Nodal Analysis) system for deriving WDF scattering matrices.

Element stamps define the internal circuit topology. The system is solved at compile time to produce the scattering matrix.

Fields§

§num_nodes: usize

Number of nodes (excluding ground)

§num_vsources: usize

Number of voltage sources / controlled sources

§g_matrix: Vec<f64>

Conductance matrix G (num_nodes × num_nodes)

§b_matrix: Vec<f64>

Voltage source matrix B (num_nodes × num_vsources)

§c_matrix: Vec<f64>

Current output matrix C (num_vsources × num_nodes)

§d_matrix: Vec<f64>

Direct coupling matrix D (num_vsources × num_vsources)

Implementations§

§

impl MnaSystem

pub fn new(num_nodes: usize, num_vsources: usize) -> MnaSystem

Create an empty MNA system.

pub fn stamp_resistor( &mut self, n1: Option<usize>, n2: Option<usize>, resistance: f64, )

Add a resistor stamp between nodes n1 and n2.

pub fn stamp_voltage_source( &mut self, n_pos: Option<usize>, n_neg: Option<usize>, vsrc_idx: usize, )

Add a voltage source stamp (vsrc_idx) between nodes n+ and n-.

pub fn stamp_transformer( &mut self, p_pos: Option<usize>, p_neg: Option<usize>, s_pos: Option<usize>, s_neg: Option<usize>, vsrc_p: usize, vsrc_s: usize, turns_ratio: f64, )

Add an ideal transformer stamp.

Primary: nodes p+ to p- (voltage source vsrc_p) Secondary: nodes s+ to s- (voltage source vsrc_s) Turns ratio: n = V_primary / V_secondary

pub fn stamp_vccs( &mut self, out_pos: Option<usize>, out_neg: Option<usize>, in_pos: Option<usize>, in_neg: Option<usize>, gm: f64, )

Add a VCCS (Voltage-Controlled Current Source) stamp.

Models I_out = gm * V_in where:

  • Input voltage: V(in_pos) - V(in_neg)
  • Output current: flows from out_pos to out_neg

MNA stamp: G[out+][in+] += gm, G[out+][in-] -= gm, G[out-][in+] -= gm, G[out-][in-] += gm

pub fn stamp_vcvs( &mut self, pos: Option<usize>, neg: Option<usize>, out_pos: Option<usize>, out_neg: Option<usize>, aol: f64, ro: f64, vsrc_idx: usize, )

Add a finite-gain VCVS (Voltage-Controlled Voltage Source) stamp for modelling an op-amp as V_out = Aol·(V_pos − V_neg) − Ro·i_vsrc.

This is the nullor (Werner 2016) generalised to finite open-loop gain and non-zero output impedance. With Aol → ∞, Ro → 0 it becomes a pure nullor (infinite-gain ideal op-amp). Typical datasheet values for audio op-amps: Aol ∈ [50k, 400k], Ro ∈ [50, 200]Ω.

Reserves one auxiliary MNA row/column (the norator branch current i_vsrc). The constraint equation written into row vsrc_idx is:

v(out_pos) − v(out_neg) − Aol·v(pos) + Aol·v(neg) + Ro·i_vsrc = 0

Stamp:

  • B[out_pos, vsrc] += +1, B[out_neg, vsrc] += −1 — branch current i_vsrc is injected into the out_pos/out_neg node pair
  • C[vsrc, out_pos] += +1, C[vsrc, out_neg] += −1 — output voltage
  • C[vsrc, pos] += −Aol, C[vsrc, neg] += +Aol — controlling voltage
  • D[vsrc, vsrc] += Ro — finite output impedance

Inputs at pos/neg draw zero current (nullator / infinite input impedance).

  • pos, neg — non-inverting and inverting input nodes (None = gnd)
  • out_pos, out_neg — output node pair (None = gnd)
  • aol — open-loop voltage gain (dimensionless)
  • ro — output resistance in Ohms (use 0.0 for ideal nullor)
  • vsrc_idx — auxiliary MNA branch index (must be < num_vsources)

pub fn dc_gain(&self, vs_idx: usize, output_pos: Option<usize>) -> f64

Derive the scattering matrix for WDF ports.

Compute the DC transfer function gain: V_out / V_in at f=0.

Solves the augmented MNA system [G, B; C, D] · [V; I] = [0; …1…] and returns the voltage at the output node when VS injects 1V. For purely resistive networks (no caps), this IS the transfer function.

pub fn derive_scattering_matrix(&self, port_resistances: &[f64]) -> Vec<f64>

Each port corresponds to a Thévenin equivalent at a node pair. The last port is adapted (reflection-free).

Returns: NxN scattering matrix in row-major order.

pub fn derive_scattering_matrix_general(&self, ports: &[WdfPort]) -> Vec<f64>

Derive scattering matrix for arbitrary port terminal pairs.

Generalizes derive_scattering_matrix to support ports that span arbitrary node pairs (not just node-to-ground). This is required for BJT collector-to-emitter ports where neither terminal is ground.

Uses the generalized Werner formula:

S[i][j] = 2/R_j · (X⁻¹[ai,aj] - X⁻¹[ai,bj] - X⁻¹[bi,aj] + X⁻¹[bi,bj]) - δ_ij

where a_k/b_k are the positive/negative terminal nodes of port k.

The last port is adapted (reflection-free, S[n-1][n-1] ≈ 0).

pub fn derive_scattering_and_vs_injection( &self, ports: &[WdfPort], vs_idx: usize, ) -> (Vec<f64>, Vec<f64>)

Derive scattering matrix and VS injection vector for ports driven by an ideal voltage source (zero internal impedance).

Unlike derive_scattering_matrix_general where the VS is a WDF port, this method stamps the VS directly into the MNA B/C/D matrices so its internal impedance is zero. The scattering matrix is derived for the remaining (reactive + probe) ports only, and a separate injection vector k maps the VS voltage to port incident waves:

a[i] = Σ_j S[i][j] · b[j] + k[i] · V_in
  • ports — WDF ports (reactive elements + output probe; no VS port)
  • vs_idx — index of the voltage source branch in the MNA system

Returns (scattering, vs_injection).

pub fn derive_extraction_coeffs( &self, ports: &[WdfPort], vs_idx: usize, output_pos: Option<usize>, output_neg: Option<usize>, ) -> (Vec<f64>, f64)

Derive node-voltage extraction coefficients for reading the output voltage at a specific MNA node from the WDF port b-waves and VS input.

The output voltage is computed as:

V(node) = Σ_k extract[k] * b[k] + extract_vs * V_in

This bypasses WDF port impedance mismatch by reading the circuit’s nodal voltage directly from the X⁻¹ matrix.

  • ports — WDF ports (same as passed to scattering derivation)
  • vs_idx — index of the voltage source branch in the MNA system
  • output_pos — positive output MNA node (Some(idx) or None for ground)
  • output_neg — negative output MNA node (Some(idx) or None for ground)

Returns (port_coeffs, vs_coeff).

pub fn derive_node_extraction_coeffs( &self, ports: &[WdfPort], output_pos: Option<usize>, output_neg: Option<usize>, ) -> Vec<f64>

Derive node-voltage extraction coefficients for reading the output voltage at a specific MNA node from the WDF port b-waves, without a voltage source.

This is the same computation as derive_extraction_coeffs but omits the VS term. Use this for standard adapted-WDF-port stages (non-VS-injection mode) where the adapted port is itself the last element of ports.

Returns port_coeffs where V(out) = Σ_k port_coeffs[k] * b[k].

pub fn build_iir( &self, reactive_one_ports: &[OnePort<DomainIndex<MnaNodeDomain>>], _vs_idx: usize, _output_pos: Option<usize>, _output_neg: Option<usize>, sample_rate: f64, feedback_r: Option<(f64, f64, f64)>, ) -> Option<(Vec<f64>, Vec<f64>)>

Build discrete-time state-space matrices from the continuous-time MNA using the bilinear (trapezoidal) transform.

This avoids WDF port-impedance scaling issues by working directly with node voltages. Capacitors are treated as continuous-time elements (C matrix) rather than WDF ports (port conductance 1/R_p = 2·f_s·C).

The continuous-time augmented system is:

[G   B] [V ]   [C_cap  0] [dV/dt]   [0  ]
[E   D] [I ] + [0      0] [dI/dt] = [V_s]

Compile a linear MNA circuit to IIR filter coefficients.

For stable circuits: derives the transfer function H(z) from the MNA and returns IIR coefficients directly. Order = number of caps.

For unstable circuits (oscillators): detects the instability via eigenvalue analysis, computes f0 from cap/R values and Q from the loop gain margin, and returns a biquad IIR.

Returns Some((b_coeffs, a_coeffs)) where b is the numerator and a is the denominator (a[0] = 1.0, normalized). Returns None if the circuit can’t be compiled to IIR.

pub fn build_state_space_matrices( &self, reactive_one_ports: &[OnePort<DomainIndex<MnaNodeDomain>>], vs_idx: usize, output_pos: Option<usize>, output_neg: Option<usize>, sample_rate: f64, ) -> (Vec<f64>, Vec<f64>, Vec<f64>, usize, f64)

Bilinear transform s → 2·f_s·(z-1)/(z+1) gives:

M · x[n] = N · x[n-1] + F · u[n]

where M = [G+2fsC B; E D], N = [2fsC-G -B; 0 0], and F = [0...1...] maps the VS input.

Returns (A_d, b_d, c_out, n_states) where:

  • A_d = M⁻¹·N (n×n state transition matrix)
  • b_d = M⁻¹·F (n×1 input vector)
  • c_out (1×n output extraction vector)
  • n_states = num_nodes + num_vsources

Trait Implementations§

§

impl Clone for MnaSystem

§

fn clone(&self) -> MnaSystem

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for MnaSystem

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more