Source code for tidy3d.components.spice.sources.dc

"""
Our DC sources ultimately need to follow this standard form if we want to enable full electrical integration.

```
11.3.2 .DC: DC Transfer Function

General form:

    .dc srcnam vstart vstop vincr [src2 start2 stop2 incr2]

Examples:

    .dc VIN 0.25 5.0 0.25
    .dc VDS 0 10 .5 VGS 0 5 1
    .dc VCE 0 10 .25 IB 0 10u 1u
    .dc RLoad 1k 2k 100
    .dc TEMP -15 75 5
```

"""

from __future__ import annotations

from typing import TYPE_CHECKING, Literal

import numpy as np
from pydantic import Field, FiniteFloat, field_validator

from tidy3d.components.base import Tidy3dBaseModel
from tidy3d.components.types import ArrayFloat1D
from tidy3d.constants import AMP, VOLT, inf
from tidy3d.log import log

if TYPE_CHECKING:
    from numpy.typing import NDArray


[docs] class DCVoltageSource(Tidy3dBaseModel): """ DC voltage source in volts. Notes ----- This voltage refers to potential above the equivalent simulation ground. Currently, electrical ports are not defined. Examples -------- >>> import tidy3d as td >>> voltages = [-0.5, 0, 1, 2, 3, 4] >>> voltage_source = td.DCVoltageSource(voltage=voltages) """ name: str | None = Field( None, title="Name", description="Unique name for the DC voltage source", min_length=1, ) voltage: ArrayFloat1D = Field( title="Voltage", description="DC voltage usually used as source in :class:`VoltageBC` boundary conditions.", json_schema_extra={"units": VOLT}, ) # TODO: This should have always been in the field above but was introduced wrongly as a # standalone field. Keeping for compatibility, remove in 3.0. units: Literal[VOLT] = VOLT
[docs] @field_validator("voltage") @classmethod def check_voltage(cls, val: ArrayFloat1D) -> ArrayFloat1D: for v in val: if v == inf: raise ValueError(f"Voltages must be finite. Currently voltage={val}.") return val
@staticmethod def _count_unique_with_tolerance(arr: NDArray, rtol: float = 1e-9, atol: float = 1e-12) -> int: """Count unique values treating values within tolerance as duplicates. Uses sorted comparison to group values that are practically equal due to floating-point representation differences (e.g., single vs double precision). """ if len(arr) == 0: return 0 sorted_arr = np.sort(arr) # Count values that are "different enough" from their predecessor unique_count = 1 for i in range(1, len(sorted_arr)): if not np.isclose(sorted_arr[i], sorted_arr[i - 1], rtol=rtol, atol=atol): unique_count += 1 return unique_count
[docs] @field_validator("voltage") @classmethod def check_repeated_voltage(cls, val: ArrayFloat1D) -> ArrayFloat1D: """Warn if repeated voltage values are present, treating 0 and -0 as the same value. Uses tolerance-based comparison to handle floating-point representation differences (e.g., values from single vs double precision sources). """ # Normalize all zero values (both 0.0 and -0.0) to 0.0 so they are treated as duplicates normalized = np.where(np.isclose(val, 0, atol=1e-10), 0.0, val) unique_count = cls._count_unique_with_tolerance(normalized) if unique_count < len(val): log.warning( "Duplicate voltage values detected in 'voltage' array. " f"Found {len(val)} values but only {unique_count} are unique. " "Note: values within floating-point tolerance are considered duplicates." ) return val
[docs] class GroundVoltage(Tidy3dBaseModel): """ Ground voltage source (0V reference). Notes ----- This source explicitly sets the ground reference (0V) for the simulation. It is equivalent to :class:`DCVoltageSource(voltage=0)` but more explicit about establishing the ground reference. If no :class:`GroundVoltage` is specified, the smallest voltage among all sources will be considered as the ground reference. Note that the boundary conditions defined using a voltage array will be ignored during this process and cannot be used as a default ground. Example ------- >>> import tidy3d as td >>> ground_source = td.GroundVoltage() >>> voltage_bc = td.VoltageBC(source=ground_source) """
[docs] class DCCurrentSource(Tidy3dBaseModel): """ DC current source in amperes. Example ------- >>> import tidy3d as td >>> current_source = td.DCCurrentSource(current=0.4) """ name: str | None = Field( None, title="Name", description="Unique name for the DC current source", min_length=1, ) current: FiniteFloat = Field( title="Current", description="DC current usually used as source in :class:`CurrentBC` boundary conditions.", json_schema_extra={"units": AMP}, ) # TODO: This should have always been in the field above but was introduced wrongly as a # standalone field. Keeping for compatibility, remove in 3.0. units: Literal[AMP] = AMP