Skip to content

Commit 09478f6

Browse files
committed
Updated comments and small fixes
1 parent 581c7ee commit 09478f6

File tree

2 files changed

+94
-12
lines changed

2 files changed

+94
-12
lines changed

tests/test_components/test_heat_charge.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from matplotlib import pyplot as plt
99

1010
import tidy3d as td
11+
from tidy3d.components.tcad.simulation.heat_charge import TCADAnalysisTypes
1112
from tidy3d.components.tcad.types import (
1213
AugerRecombination,
1314
CaugheyThomasMobility,
@@ -2513,3 +2514,88 @@ def test_generation_recombination():
25132514
beta_n=1,
25142515
beta_p=1,
25152516
)
2517+
2518+
2519+
def test_heat_only_simulation_with_semiconductor():
2520+
"""Test that a heat-only simulation with semiconductors does not trigger charge simulation.
2521+
Charge simulations are only triggered when `analysis_spec` is provided, not just when
2522+
semiconductors are present in the simulation.
2523+
"""
2524+
2525+
# Create a semiconductor medium
2526+
semiconductor_medium = td.MultiPhysicsMedium(
2527+
optical=td.Medium(permittivity=5, conductivity=0.01),
2528+
heat=td.SolidMedium(conductivity=3, capacity=2),
2529+
charge=td.SemiconductorMedium(
2530+
N_c=td.ConstantEffectiveDOS(N=1e10),
2531+
N_v=td.ConstantEffectiveDOS(N=1e10),
2532+
E_g=td.ConstantEnergyBandGap(eg=1),
2533+
mobility_n=td.ConstantMobilityModel(mu=1500),
2534+
mobility_p=td.ConstantMobilityModel(mu=1500),
2535+
),
2536+
name="semiconductor",
2537+
)
2538+
2539+
# Create a non-semiconductor solid medium
2540+
solid_medium = td.MultiPhysicsMedium(
2541+
optical=td.Medium(permittivity=5, conductivity=0.01),
2542+
heat=td.SolidMedium(conductivity=1, capacity=1),
2543+
charge=td.ChargeConductorMedium(conductivity=1),
2544+
name="solid",
2545+
)
2546+
2547+
# Create structures with both semiconductor and other materials
2548+
semiconductor_structure = td.Structure(
2549+
geometry=td.Box(center=(-0.5, 0, 0), size=(1, 1, 1)),
2550+
medium=semiconductor_medium,
2551+
name="semiconductor_structure",
2552+
)
2553+
2554+
solid_structure = td.Structure(
2555+
geometry=td.Box(center=(0.5, 0, 0), size=(1, 1, 1)),
2556+
medium=solid_medium,
2557+
name="solid_structure",
2558+
)
2559+
2560+
# Create heat-only boundary conditions (no electric BCs)
2561+
thermal_bc = td.HeatChargeBoundarySpec(
2562+
condition=td.TemperatureBC(temperature=300),
2563+
placement=td.StructureBoundary(structure="solid_structure"),
2564+
)
2565+
2566+
# Create heat source
2567+
heat_source = td.HeatSource(structures=["solid_structure"], rate=100)
2568+
2569+
# Create heat monitor (no charge monitors)
2570+
temp_monitor = td.TemperatureMonitor(
2571+
center=(0, 0, 0), size=(2, 1, 1), name="temp_monitor", unstructured=True
2572+
)
2573+
2574+
# Create heat-only simulation (no analysis_spec, no electric BCs)
2575+
heat_sim = td.HeatChargeSimulation(
2576+
medium=td.MultiPhysicsMedium(
2577+
heat=td.FluidMedium(), charge=td.ChargeInsulatorMedium(), name="air"
2578+
),
2579+
structures=[semiconductor_structure, solid_structure],
2580+
center=(0, 0, 0),
2581+
size=(3, 3, 3),
2582+
boundary_spec=[thermal_bc],
2583+
grid_spec=td.UniformUnstructuredGrid(dl=0.1),
2584+
sources=[heat_source],
2585+
monitors=[temp_monitor],
2586+
# No analysis_spec - this is key: without ChargeTypes analysis_spec,
2587+
# charge simulation should NOT be triggered even though semiconductors are present
2588+
)
2589+
2590+
# Verify that only HEAT simulation type is returned, not CHARGE
2591+
simulation_types = heat_sim._get_simulation_types()
2592+
assert TCADAnalysisTypes.HEAT in simulation_types, (
2593+
"Heat simulation should be triggered when heat sources/BCs are present."
2594+
)
2595+
assert TCADAnalysisTypes.CHARGE not in simulation_types, (
2596+
"Charge simulation should NOT be triggered when ChargeTypes analysis_spec is not provided, "
2597+
"even if semiconductors are present in the simulation."
2598+
)
2599+
assert TCADAnalysisTypes.CONDUCTION not in simulation_types, (
2600+
"Conduction simulation should NOT be triggered when no electric BCs are present."
2601+
)

tidy3d/components/tcad/simulation/heat_charge.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -730,14 +730,10 @@ def check_charge_simulation(cls, values):
730730
)
731731
# check that we have at least one semiconductor medium
732732
structures = values["structures"]
733-
sc_present = False
734-
for structure in structures:
735-
if isinstance(structure.medium.charge, SemiconductorMedium):
736-
sc_present = True
737-
break
733+
sc_present = HeatChargeSimulation._check_if_semiconductor_present(structures=structures)
738734
if not sc_present:
739735
raise SetupError(
740-
f"{TCADAnalysisTypes.CHARGE} simulations require the definition of, at least, one semiconductor medium."
736+
f"{TCADAnalysisTypes.CHARGE} simulations require the definition of at least one semiconductor medium."
741737
)
742738
return values
743739

@@ -910,7 +906,7 @@ def _check_simulation_types(
910906
if isinstance(boundary.condition, HeatBCTypes):
911907
simulation_types.append(TCADAnalysisTypes.HEAT)
912908
if isinstance(boundary.condition, ElectricBCTypes):
913-
# type CONDUCTION if we have no semiconductors
909+
# Add CONDUCTION type if we have no semiconductors
914910
if not semiconductor_present:
915911
simulation_types.append(TCADAnalysisTypes.CONDUCTION)
916912

@@ -1075,7 +1071,7 @@ def check_transient_heat(cls, values):
10751071
raise SetupError(
10761072
f"Unsteady simulations require the temperature monitor '{mnt.name}' to be unstructured."
10771073
)
1078-
# additionaly check that the SolidSpec has capacity and density defined
1074+
# additionally check that the SolidSpec has capacity and density defined
10791075
capacities = []
10801076
densities = []
10811077
conductivities = []
@@ -1130,7 +1126,7 @@ def check_transient_heat(cls, values):
11301126

11311127
@pd.root_validator(skip_on_failure=True)
11321128
def check_non_isothermal_is_possible(cls, values):
1133-
"""Make sure that when a non-isothermal case is defined the structrures
1129+
"""Make sure that when a non-isothermal case is defined the structures
11341130
have both electrical and thermal properties."""
11351131

11361132
analysis_spec = values.get("analysis_spec")
@@ -1507,7 +1503,7 @@ def _construct_forward_boundaries(
15071503
) -> tuple[tuple[HeatChargeBoundarySpec, Shapely], ...]:
15081504
"""Construct Simulation, StructureSimulation, Structure, and MediumMedium boundaries."""
15091505

1510-
# forward foop to take care of Simulation, StructureSimulation, Structure,
1506+
# forward loop to take care of Simulation, StructureSimulation, Structure,
15111507
# and MediumMediums
15121508
boundaries = [] # bc_spec, structure name, shape, bounds
15131509
background_shapes = []
@@ -1598,7 +1594,7 @@ def _construct_reverse_boundaries(
15981594
) -> tuple[tuple[HeatChargeBoundarySpec, Shapely], ...]:
15991595
"""Construct StructureStructure boundaries."""
16001596

1601-
# backward foop to take care of StructureStructure
1597+
# backward loop to take care of StructureStructure
16021598
# we do it in this way because we define the boundary between
16031599
# two overlapping structures A and B, where A comes before B, as
16041600
# boundary(B) intersected by A
@@ -1705,7 +1701,7 @@ def _construct_heat_charge_boundaries(
17051701

17061702
# construct boundaries in 2 passes:
17071703

1708-
# 1. forward foop to take care of Simulation, StructureSimulation, Structure,
1704+
# 1. forward loop to take care of Simulation, StructureSimulation, Structure,
17091705
# and MediumMediums
17101706
boundaries = HeatChargeSimulation._construct_forward_boundaries(
17111707
shapes=shapes,

0 commit comments

Comments
 (0)