Source code for reasitic.exports.spice

"""SPICE-style sub-circuit emitter.

Renders a Pi-equivalent inductor model as an Ngspice/HSPICE
``.subckt`` block:

::

    .subckt L1_pi p1 p2 gnd
    Rseries p1 nA  R={R_series}
    Lseries nA p2  L={L_series_nH}n
    Cp1 p1 gnd C={C_p1_fF}f
    Cp2 p2 gnd C={C_p2_fF}f
    .ends

The model is single-frequency (taken from the Pi-extraction at
the chosen analysis frequency). For broadband fitting use
multiple sub-circuits at different frequencies.

Mirrors the ``2Port`` ``S|Y|PI`` text output but in a more
useful interchange form for SPICE-flavour simulators.
"""

from __future__ import annotations

from io import StringIO
from pathlib import Path

from reasitic.geometry import Shape
from reasitic.network.analysis import pi_model_at_freq
from reasitic.tech import Tech


[docs] def write_spice_subckt( shape: Shape, tech: Tech, freq_ghz: float, *, name: str | None = None, ) -> str: """Emit a SPICE ``.subckt`` block for ``shape`` at ``freq_ghz``.""" pi = pi_model_at_freq(shape, tech, freq_ghz) sub_name = (name or shape.name) + "_pi" out = StringIO() out.write( f"* reASITIC SPICE Pi-model for <{shape.name}> at {freq_ghz:g} GHz\n" ) out.write(f".subckt {sub_name} p1 p2 gnd\n") out.write(f"Rseries p1 nA {pi.R_series:.6g}\n") out.write(f"Lseries nA p2 {pi.L_nH:.6g}n\n") out.write(f"Cp1 p1 gnd {pi.C_p1_fF:.6g}f\n") out.write(f"Cp2 p2 gnd {pi.C_p2_fF:.6g}f\n") if pi.g_p1 > 0: out.write(f"Rsub1 p1 gnd {1.0 / pi.g_p1:.6g}\n") if pi.g_p2 > 0: out.write(f"Rsub2 p2 gnd {1.0 / pi.g_p2:.6g}\n") out.write(".ends\n") return out.getvalue()
[docs] def write_spice_subckt_file( path: str | Path, shape: Shape, tech: Tech, freq_ghz: float, *, name: str | None = None, ) -> None: """Write the SPICE sub-circuit to ``path``.""" Path(path).write_text(write_spice_subckt(shape, tech, freq_ghz, name=name))
[docs] def write_spice_broadband( shape: Shape, tech: Tech, freqs_ghz: list[float], *, name: str | None = None, ) -> str: """Emit a frequency-dependent SPICE model. Each frequency in ``freqs_ghz`` produces a separate ``.subckt`` block named ``<shape>_pi_<f_GHz>``. Useful when a single Pi-model isn't enough — pair with a SPICE ``.alter`` or per-band selection. """ out: list[str] = [] for f in freqs_ghz: sub_name = (name or shape.name) + f"_pi_{f:g}GHz" out.append(write_spice_subckt(shape, tech, f, name=sub_name)) return "\n".join(out)
[docs] def write_spice_broadband_file( path: str | Path, shape: Shape, tech: Tech, freqs_ghz: list[float], *, name: str | None = None, ) -> None: """Write the broadband SPICE rendering to ``path``.""" Path(path).write_text(write_spice_broadband(shape, tech, freqs_ghz, name=name))