Skip to content

Commit ddaaccb

Browse files
committed
Non-isothermal charge simulations
1 parent 6239df6 commit ddaaccb

File tree

5 files changed

+126
-26
lines changed

5 files changed

+126
-26
lines changed

tests/test_components/test_heat_charge.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,23 +1170,30 @@ def Si_p(self):
11701170
semiconductor = CHARGE_SIMULATION.intrinsic_Si.charge
11711171
semiconductor = semiconductor.updated_copy(
11721172
N_a=CHARGE_SIMULATION.acceptors,
1173+
)
1174+
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(
1175+
charge=semiconductor,
1176+
heat=td.SolidMedium(conductivity=1),
11731177
name="Si_p",
11741178
)
1175-
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(charge=semiconductor)
11761179

11771180
@pytest.fixture(scope="class")
11781181
def Si_n(self):
11791182
semiconductor = CHARGE_SIMULATION.intrinsic_Si.charge
11801183
semiconductor = semiconductor.updated_copy(
11811184
N_d=CHARGE_SIMULATION.donors,
1185+
)
1186+
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(
1187+
charge=semiconductor,
1188+
heat=td.SolidMedium(conductivity=1),
11821189
name="Si_n",
11831190
)
1184-
return CHARGE_SIMULATION.intrinsic_Si.updated_copy(charge=semiconductor)
11851191

11861192
@pytest.fixture(scope="class")
11871193
def SiO2(self):
11881194
return td.MultiPhysicsMedium(
11891195
charge=td.ChargeInsulatorMedium(permittivity=3.9),
1196+
heat=td.SolidMedium(conductivity=2),
11901197
name="SiO2",
11911198
)
11921199

@@ -1267,18 +1274,13 @@ def capacitance_global_mnt(self):
12671274
# Define charge settings as fixtures within the class
12681275
@pytest.fixture(scope="class")
12691276
def charge_tolerance(self):
1270-
return td.IsothermalSteadyChargeDCAnalysis(
1271-
temperature=300,
1272-
tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400),
1273-
fermi_dirac=True,
1274-
)
1275-
1276-
@pytest.fixture(scope="class")
1277-
def charge_dc_regime(self):
1278-
return td.DCVoltageSource(voltage=[1])
1277+
return td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400)
12791278

12801279
def test_charge_simulation(
12811280
self,
1281+
Si_n,
1282+
Si_p,
1283+
SiO2,
12821284
oxide,
12831285
p_side,
12841286
n_side,
@@ -1288,9 +1290,14 @@ def test_charge_simulation(
12881290
bc_n,
12891291
bc_p,
12901292
charge_tolerance,
1291-
charge_dc_regime,
12921293
):
12931294
"""Ensure charge simulation produces the correct errors when needed."""
1295+
# NOTE: start tests with isothermal spec
1296+
isothermal_spec = td.IsothermalSteadyChargeDCAnalysis(
1297+
temperature=300,
1298+
tolerance_settings=charge_tolerance,
1299+
fermi_dirac=True,
1300+
)
12941301
sim = td.HeatChargeSimulation(
12951302
structures=[oxide, p_side, n_side],
12961303
medium=td.MultiPhysicsMedium(
@@ -1301,7 +1308,7 @@ def test_charge_simulation(
13011308
size=CHARGE_SIMULATION.sim_size,
13021309
grid_spec=td.UniformUnstructuredGrid(dl=0.05),
13031310
boundary_spec=[bc_n, bc_p],
1304-
analysis_spec=charge_tolerance,
1311+
analysis_spec=isothermal_spec,
13051312
)
13061313

13071314
# At least one ChargeSimulationMonitor should be added
@@ -1336,6 +1343,45 @@ def test_charge_simulation(
13361343
)
13371344
_ = sim.updated_copy(boundary_spec=[new_bc_p, bc_n])
13381345

1346+
# test non isothermal spec
1347+
non_isothermal_spec = td.SteadyChargeDCAnalysis(tolerance_settings=charge_tolerance)
1348+
1349+
sim = sim.updated_copy(analysis_spec=non_isothermal_spec)
1350+
with pytest.raises(pd.ValidationError):
1351+
# remove heat from mediums
1352+
new_structs = []
1353+
for struct in sim.structures:
1354+
new_structs.append(
1355+
struct.updated_copy(medium=struct.medium.updated_copy(heat=None))
1356+
)
1357+
_ = sim.updated_copy(structures=new_structs)
1358+
1359+
with pytest.raises(pd.ValidationError):
1360+
# remove charge from mediums
1361+
new_structs = []
1362+
for struct in sim.structures:
1363+
new_structs.append(
1364+
struct.updated_copy(medium=struct.medium.updated_copy(charge=None))
1365+
)
1366+
_ = sim.updated_copy(structures=new_structs)
1367+
1368+
with pytest.raises(pd.ValidationError):
1369+
# make sure there is at least one semiconductor
1370+
new_structs = []
1371+
for struct in sim.structures:
1372+
if isinstance(struct.medium.charge, td.SemiconductorMedium):
1373+
new_structs.append(
1374+
struct.updated_copy(
1375+
medium=struct.medium.updated_copy(
1376+
charge=td.ChargeInsulatorMedium(permittivity=1),
1377+
heat=None,
1378+
)
1379+
)
1380+
)
1381+
else:
1382+
new_structs.append(struct)
1383+
_ = sim.updated_copy(structures=new_structs)
1384+
13391385
def test_doping_distributions(self):
13401386
"""Test doping distributions."""
13411387
# Implementation needed

tidy3d/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from tidy3d.components.spice.analysis.dc import (
2121
ChargeToleranceSpec,
2222
IsothermalSteadyChargeDCAnalysis,
23+
SteadyChargeDCAnalysis,
2324
)
2425
from tidy3d.components.spice.sources.dc import DCCurrentSource, DCVoltageSource
2526
from tidy3d.components.spice.sources.types import VoltageSourceType
@@ -663,6 +664,7 @@ def set_logging_level(level: str) -> None:
663664
"Staircasing",
664665
"SteadyCapacitanceData",
665666
"SteadyCapacitanceMonitor",
667+
"SteadyChargeDCAnalysis",
666668
"SteadyElectricFieldData",
667669
"SteadyElectricFieldMonitor",
668670
"SteadyEnergyBandData",

tidy3d/components/spice/analysis/dc.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,11 @@ class ChargeToleranceSpec(Tidy3dBaseModel):
4949
)
5050

5151

52-
class IsothermalSteadyChargeDCAnalysis(Tidy3dBaseModel):
52+
class SteadyChargeDCAnalysis(Tidy3dBaseModel):
5353
"""
5454
Configures relevant steady-state DC simulation parameters for a charge simulation.
5555
"""
5656

57-
temperature: pd.PositiveFloat = pd.Field(
58-
300,
59-
title="Temperature",
60-
description="Lattice temperature. Assumed constant throughout the device. "
61-
"Carriers are assumed to be at thermodynamic equilibrium with the lattice.",
62-
units=KELVIN,
63-
)
64-
6557
tolerance_settings: ChargeToleranceSpec = pd.Field(
6658
default=ChargeToleranceSpec(), title="Tolerance settings"
6759
)
@@ -83,3 +75,17 @@ class IsothermalSteadyChargeDCAnalysis(Tidy3dBaseModel):
8375
"where very high doping may lead the pseudo-Fermi energy level to approach "
8476
"either the conduction or valence energy bands.",
8577
)
78+
79+
80+
class IsothermalSteadyChargeDCAnalysis(SteadyChargeDCAnalysis):
81+
"""
82+
Configures relevant steady-state DC simulation parameters for a charge simulation.
83+
"""
84+
85+
temperature: pd.PositiveFloat = pd.Field(
86+
300,
87+
title="Temperature",
88+
description="Lattice temperature. Assumed constant throughout the device. "
89+
"Carriers are assumed to be at thermodynamic equilibrium with the lattice.",
90+
units=KELVIN,
91+
)

tidy3d/components/spice/types.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
from typing import Union
44

5-
from tidy3d.components.spice.analysis.dc import IsothermalSteadyChargeDCAnalysis
5+
from tidy3d.components.spice.analysis.dc import (
6+
IsothermalSteadyChargeDCAnalysis,
7+
SteadyChargeDCAnalysis,
8+
)
69

7-
ElectricalAnalysisType = Union[IsothermalSteadyChargeDCAnalysis]
10+
ElectricalAnalysisType = Union[SteadyChargeDCAnalysis, IsothermalSteadyChargeDCAnalysis]

tidy3d/components/tcad/simulation/heat_charge.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@
3535
from tidy3d.components.medium import Medium
3636
from tidy3d.components.scene import Scene
3737
from tidy3d.components.spice.sources.dc import DCVoltageSource
38-
from tidy3d.components.spice.types import ElectricalAnalysisType
38+
from tidy3d.components.spice.types import (
39+
ElectricalAnalysisType,
40+
IsothermalSteadyChargeDCAnalysis,
41+
SteadyChargeDCAnalysis,
42+
)
3943
from tidy3d.components.structure import Structure
4044
from tidy3d.components.tcad.analysis.heat_simulation_type import UnsteadyHeatAnalysis
4145
from tidy3d.components.tcad.boundary.specification import (
@@ -951,6 +955,44 @@ def check_transient_heat(cls, values):
951955
)
952956
return values
953957

958+
@pd.root_validator(skip_on_failure=True)
959+
def check_non_isothermal_is_possible(cls, values):
960+
"""Make sure that when a non-isothermal case is defined the structrures
961+
have both electrical and thermal properties."""
962+
963+
analysis_spec = values.get("analysis_spec")
964+
if isinstance(analysis_spec, SteadyChargeDCAnalysis) and not isinstance(
965+
analysis_spec, IsothermalSteadyChargeDCAnalysis
966+
):
967+
has_heat = False
968+
has_elec = False
969+
structures = values.get("structures")
970+
for struct in structures:
971+
if isinstance(struct.medium, MultiPhysicsMedium):
972+
if struct.medium.heat is not None:
973+
if isinstance(struct.medium.heat, SolidMedium):
974+
has_heat = True
975+
if struct.medium.charge is not None:
976+
if isinstance(struct.medium.charge, SemiconductorMedium):
977+
has_elec = True
978+
979+
if not has_heat and has_elec:
980+
raise SetupError(
981+
"The current simulation is defined as non-isothermal but no solid "
982+
"materials with heat properties have been defined. "
983+
)
984+
elif not has_elec and has_heat:
985+
raise SetupError(
986+
"The current simulation is defined as non-isothermal but no "
987+
"semiconductor materials have been defined. "
988+
)
989+
elif not has_heat and not has_elec:
990+
raise SetupError(
991+
"The current simulation is defined as non-isothermal but no "
992+
"solid or semiconductor materials have been defined. "
993+
)
994+
return values
995+
954996
@equal_aspect
955997
@add_ax_if_none
956998
def plot_property(
@@ -1735,7 +1777,8 @@ def _get_simulation_types(self) -> list[TCADAnalysisTypes]:
17351777

17361778
# NOTE: for the time being, if a simulation has SemiconductorMedium
17371779
# then we consider it of being a 'TCADAnalysisTypes.CHARGE'
1738-
if isinstance(self.analysis_spec, ElectricalAnalysisType):
1780+
ChargeTypes = (SteadyChargeDCAnalysis, IsothermalSteadyChargeDCAnalysis)
1781+
if isinstance(self.analysis_spec, ChargeTypes):
17391782
if self._check_if_semiconductor_present(self.structures):
17401783
return [TCADAnalysisTypes.CHARGE]
17411784

0 commit comments

Comments
 (0)