Source code for sycan.components.basic.capacitor
"""Capacitor (SPICE ``C``).
* **DC**: ideal open, i.e. not stamped.
* **AC**: admittance ``sC``.
* **Transient**: admittance ``sC`` plus an initial-condition current
injection ``C·v0`` (from ``i_C(s) = sC·V(s) − C·v0``).
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import ClassVar, Optional
from sycan import cas as cas
from sycan.mna import Component, NoiseSpec, StampContext
[docs]
@dataclass
class Capacitor(Component):
"""Linear capacitor; ``value`` is the capacitance.
``ic`` is the optional initial voltage for transient analysis,
with polarity ``v0 = V(n_plus) − V(n_minus)`` at ``t = 0⁻``.
Solver-time ``initial_conditions`` overrides win over this field.
Ideal capacitors are noiseless; ``include_noise`` is accepted for
interface uniformity but only ``None`` / ``"all"`` (which expands
to the empty set) are valid.
"""
name: str
n_plus: str
n_minus: str
value: cas.Expr
include_noise: NoiseSpec = field(default=None, kw_only=True)
ic: Optional[cas.Expr] = field(default=None, kw_only=True)
ports: ClassVar[tuple[str, ...]] = ("n_plus", "n_minus")
SUPPORTED_NOISE: ClassVar[frozenset[str]] = frozenset()
def __post_init__(self) -> None:
self.value = cas.sympify(self.value)
self.include_noise = self._normalize_noise(self.include_noise)
if self.ic is not None:
self.ic = cas.sympify(self.ic)
[docs]
def stamp(self, ctx: StampContext) -> None:
if ctx.mode == "dc":
return
Y = ctx.s * self.value
i, j = ctx.n(self.n_plus), ctx.n(self.n_minus)
if i >= 0:
ctx.A[i, i] += Y
if j >= 0:
ctx.A[j, j] += Y
if i >= 0 and j >= 0:
ctx.A[i, j] -= Y
ctx.A[j, i] -= Y
if ctx.mode == "tran":
v0 = ctx.ic(self.name)
if v0 is not None:
# i_C(s) = sC·V(s) − C·v0: the constant C·v0 term moves
# to the RHS as a current injection into n_plus.
q0 = self.value * v0
if i >= 0:
ctx.b[i] += q0
if j >= 0:
ctx.b[j] -= q0