Skip to content
80 changes: 64 additions & 16 deletions tests/test_components/test_heat_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from tidy3d.components.tcad.types import (
AugerRecombination,
CaugheyThomasMobility,
ConstantEffectiveDOS,
ConstantEnergyBandGap,
SlotboomBandGapNarrowing,
)
from tidy3d.exceptions import DataError
Expand Down Expand Up @@ -39,9 +41,9 @@ class CHARGE_SIMULATION:
permittivity=11.7,
N_d=0,
N_a=0,
N_c=2.86e19,
N_v=3.1e19,
E_g=1.11,
N_c=ConstantEffectiveDOS(N=2.86e19),
N_v=ConstantEffectiveDOS(N=3.1e19),
E_g=ConstantEnergyBandGap(eg=1.11),
mobility_n=CaugheyThomasMobility(
mu_min=52.2,
mu=1471.0,
Expand Down Expand Up @@ -952,23 +954,30 @@ def Si_p(self):
semiconductor = CHARGE_SIMULATION.intrinsic_Si.charge
semiconductor = semiconductor.updated_copy(
N_a=CHARGE_SIMULATION.acceptors,
)
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(
charge=semiconductor,
heat=td.SolidMedium(conductivity=1),
name="Si_p",
)
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(charge=semiconductor)

@pytest.fixture(scope="class")
def Si_n(self):
semiconductor = CHARGE_SIMULATION.intrinsic_Si.charge
semiconductor = semiconductor.updated_copy(
N_d=CHARGE_SIMULATION.donors,
)
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(
charge=semiconductor,
heat=td.SolidMedium(conductivity=1),
name="Si_n",
)
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(charge=semiconductor)

@pytest.fixture(scope="class")
def SiO2(self):
return td.MultiPhysicsMedium(
charge=td.ChargeInsulatorMedium(permittivity=3.9),
heat=td.SolidMedium(conductivity=2),
name="SiO2",
)

Expand Down Expand Up @@ -1049,18 +1058,13 @@ def capacitance_global_mnt(self):
# Define charge settings as fixtures within the class
@pytest.fixture(scope="class")
def charge_tolerance(self):
return td.IsothermalSteadyChargeDCAnalysis(
temperature=300,
tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400),
fermi_dirac=True,
)

@pytest.fixture(scope="class")
def charge_dc_regime(self):
return td.DCVoltageSource(voltage=[1])
return td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400)

def test_charge_simulation(
self,
Si_n,
Si_p,
SiO2,
oxide,
p_side,
n_side,
Expand All @@ -1070,9 +1074,14 @@ def test_charge_simulation(
bc_n,
bc_p,
charge_tolerance,
charge_dc_regime,
):
"""Ensure charge simulation produces the correct errors when needed."""
# NOTE: start tests with isothermal spec
isothermal_spec = td.IsothermalSteadyChargeDCAnalysis(
temperature=300,
tolerance_settings=charge_tolerance,
fermi_dirac=True,
)
sim = td.HeatChargeSimulation(
structures=[oxide, p_side, n_side],
medium=td.MultiPhysicsMedium(
Expand All @@ -1083,7 +1092,7 @@ def test_charge_simulation(
size=CHARGE_SIMULATION.sim_size,
grid_spec=td.UniformUnstructuredGrid(dl=0.05),
boundary_spec=[bc_n, bc_p],
analysis_spec=charge_tolerance,
analysis_spec=isothermal_spec,
)

# At least one ChargeSimulationMonitor should be added
Expand Down Expand Up @@ -1118,6 +1127,45 @@ def test_charge_simulation(
)
_ = sim.updated_copy(boundary_spec=[new_bc_p, bc_n])

# test non isothermal spec
non_isothermal_spec = td.SteadyChargeDCAnalysis(tolerance_settings=charge_tolerance)

sim = sim.updated_copy(analysis_spec=non_isothermal_spec)
with pytest.raises(pd.ValidationError):
# remove heat from mediums
new_structs = []
for struct in sim.structures:
new_structs.append(
struct.updated_copy(medium=struct.medium.updated_copy(heat=None))
)
_ = sim.updated_copy(structures=new_structs)

with pytest.raises(pd.ValidationError):
# remove charge from mediums
new_structs = []
for struct in sim.structures:
new_structs.append(
struct.updated_copy(medium=struct.medium.updated_copy(charge=None))
)
_ = sim.updated_copy(structures=new_structs)

with pytest.raises(pd.ValidationError):
# make sure there is at least one semiconductor
new_structs = []
for struct in sim.structures:
if isinstance(struct.medium.charge, td.SemiconductorMedium):
new_structs.append(
struct.updated_copy(
medium=struct.medium.updated_copy(
charge=td.ChargeInsulatorMedium(permittivity=1),
heat=None,
)
)
)
else:
new_structs.append(struct)
_ = sim.updated_copy(structures=new_structs)

def test_doping_distributions(self):
"""Test doping distributions."""
# Implementation needed
Expand Down
16 changes: 16 additions & 0 deletions tidy3d/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from tidy3d.components.spice.analysis.dc import (
ChargeToleranceSpec,
IsothermalSteadyChargeDCAnalysis,
SteadyChargeDCAnalysis,
)
from tidy3d.components.spice.sources.dc import DCCurrentSource, DCVoltageSource
from tidy3d.components.spice.sources.types import VoltageSourceType
Expand Down Expand Up @@ -63,12 +64,19 @@
AugerRecombination,
CaugheyThomasMobility,
ConstantMobilityModel,
ConstantEffectiveDOS,
ConstantEnergyBandGap,
QuadraticEnergyBandGap,
VarshniEnergyBandGap,
ConvectionBC,
CurrentBC,
DualValleyEffectiveDOS,
HeatFluxBC,
HeatFromElectricSource,
HeatSource,
InsulatingBC,
IsotropicEffectiveDOS,
MultiValleyEffectiveDOS,
RadiativeRecombination,
ShockleyReedHallRecombination,
SlotboomBandGapNarrowing,
Expand Down Expand Up @@ -441,6 +449,10 @@ def set_logging_level(level: str) -> None:
"CoaxialLumpedResistor",
"ConstantDoping",
"ConstantMobilityModel",
"ConstantEffectiveDOS",
"IsotropicEffectiveDOS",
"MultiValleyEffectiveDOS",
"DualValleyEffectiveDOS",
"ContinuousWave",
"ContinuousWaveTimeModulation",
"ContourPathAveraging",
Expand Down Expand Up @@ -651,6 +663,7 @@ def set_logging_level(level: str) -> None:
"Staircasing",
"SteadyCapacitanceData",
"SteadyCapacitanceMonitor",
"SteadyChargeDCAnalysis",
"SteadyEnergyBandData",
"SteadyEnergyBandMonitor",
"SteadyFreeCarrierData",
Expand Down Expand Up @@ -696,4 +709,7 @@ def set_logging_level(level: str) -> None:
"set_logging_console",
"set_logging_file",
"wavelengths",
"ConstantEnergyBandGap",
"QuadraticEnergyBandGap",
"VarshniEnergyBandGap",
]
Comment on lines 709 to 715
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider grouping energy band gap models together with the other models in the first import block rather than appending to end of all

8 changes: 5 additions & 3 deletions tidy3d/components/material/tcad/charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from tidy3d.components.tcad.doping import DopingBoxType
from tidy3d.components.tcad.types import (
BandGapNarrowingModelType,
EffectiveDOSModelType,
EnergyBandGapModelType,
MobilityModelType,
RecombinationModelType,
)
Expand Down Expand Up @@ -254,21 +256,21 @@ class SemiconductorMedium(AbstractChargeMedium):

"""

N_c: pd.PositiveFloat = pd.Field(
N_c: EffectiveDOSModelType = pd.Field(
...,
title="Effective density of electron states",
description=r"$N_c$ Effective density of states in the conduction band.",
units="cm^(-3)",
)

N_v: pd.PositiveFloat = pd.Field(
N_v: EffectiveDOSModelType = pd.Field(
...,
title="Effective density of hole states",
description=r"$N_v$ Effective density of states in the valence band.",
units="cm^(-3)",
)

E_g: pd.PositiveFloat = pd.Field(
E_g: EnergyBandGapModelType = pd.Field(
...,
title="Band-gap energy",
description="Band-gap energy",
Expand Down
24 changes: 15 additions & 9 deletions tidy3d/components/spice/analysis/dc.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,11 @@ class ChargeToleranceSpec(Tidy3dBaseModel):
)


class IsothermalSteadyChargeDCAnalysis(Tidy3dBaseModel):
class SteadyChargeDCAnalysis(Tidy3dBaseModel):
"""
Configures relevant steady-state DC simulation parameters for a charge simulation.
"""

temperature: pd.PositiveFloat = pd.Field(
300,
title="Temperature",
description="Lattice temperature. Assumed constant throughout the device. "
"Carriers are assumed to be at thermodynamic equilibrium with the lattice.",
units=KELVIN,
)

tolerance_settings: ChargeToleranceSpec = pd.Field(
default=ChargeToleranceSpec(), title="Tolerance settings"
)
Expand All @@ -83,3 +75,17 @@ class IsothermalSteadyChargeDCAnalysis(Tidy3dBaseModel):
"where very high doping may lead the pseudo-Fermi energy level to approach "
"either the conduction or valence energy bands.",
)


class IsothermalSteadyChargeDCAnalysis(SteadyChargeDCAnalysis):
"""
Configures relevant steady-state DC simulation parameters for a charge simulation.
"""

temperature: pd.PositiveFloat = pd.Field(
300,
title="Temperature",
description="Lattice temperature. Assumed constant throughout the device. "
"Carriers are assumed to be at thermodynamic equilibrium with the lattice.",
units=KELVIN,
)
7 changes: 5 additions & 2 deletions tidy3d/components/spice/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

from typing import Union

from tidy3d.components.spice.analysis.dc import IsothermalSteadyChargeDCAnalysis
from tidy3d.components.spice.analysis.dc import (
IsothermalSteadyChargeDCAnalysis,
SteadyChargeDCAnalysis,
)

ElectricalAnalysisType = Union[IsothermalSteadyChargeDCAnalysis]
ElectricalAnalysisType = Union[SteadyChargeDCAnalysis, IsothermalSteadyChargeDCAnalysis]
Loading