Skip to content

Commit 83f870b

Browse files
committed
Last fix
1 parent 3494f6d commit 83f870b

File tree

5 files changed

+184
-10
lines changed

5 files changed

+184
-10
lines changed

tests/test_components/test_heat.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ def test_heat_bcs():
125125
with pytest.raises(pd.ValidationError):
126126
_ = ConvectionBC(ambient_temperature=400, transfer_coeff=-0.2)
127127

128+
# Test vertical natural convection model in ConvectionBC
129+
air = td.MultiPhysicsMedium(
130+
heat=td.FluidMedium.from_si_units(
131+
thermal_conductivity=0.026,
132+
viscosity=1.8e-5,
133+
specific_heat=1005,
134+
density=1.2,
135+
expansivity=1 / 300.0,
136+
),
137+
name="air",
138+
)
139+
140+
with pytest.raises(pd.ValidationError):
141+
td.VerticalNaturalConvectionCoeffModel(medium=air, plate_length=-10)
142+
143+
_, solid_medium = make_heat_mediums()
144+
with pytest.raises(pd.ValidationError):
145+
td.VerticalNaturalConvectionCoeffModel(medium=solid_medium, plate_length=1e5)
146+
128147

129148
def make_heat_mnts():
130149
temp_mnt1 = TemperatureMonitor(size=(1.6, 2, 3), name="test")

tests/test_components/test_heat_charge.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,132 @@ def test_heat_charge_bcs_validation(boundary_conditions):
878878
td.VoltageBC(source=td.DCVoltageSource(voltage=np.array([td.inf, 0, 1])))
879879

880880

881+
def test_vertical_natural_convection():
882+
solid_box_l = td.Box(center=(0, 0, 0), size=(2, 2, 2))
883+
solid_box_r = td.Box(center=(1, 1, 1), size=(2, 2, 2))
884+
fluid_box_r = td.Box(center=(1, 1, 1), size=(2, 2, 2))
885+
886+
solid_medium = td.MultiPhysicsMedium(
887+
heat=td.SolidMedium(conductivity=1, capacity=1), name="solid"
888+
)
889+
air = td.MultiPhysicsMedium(
890+
heat=td.FluidMedium.from_si_units(
891+
thermal_conductivity=0.026,
892+
viscosity=1.8e-5,
893+
specific_heat=1005,
894+
density=1.2,
895+
expansivity=1 / 300.0,
896+
),
897+
name="air",
898+
)
899+
solid_structure_l = td.Structure(
900+
geometry=solid_box_l,
901+
medium=solid_medium,
902+
name="solid_l",
903+
)
904+
solid_structure_r = td.Structure(
905+
geometry=solid_box_r,
906+
medium=solid_medium,
907+
name="solid_r",
908+
)
909+
fluid_structure_r = td.Structure(
910+
geometry=fluid_box_r,
911+
medium=air,
912+
name="fluid_r",
913+
)
914+
915+
coeff_model = td.VerticalNaturalConvectionCoeffModel(plate_length=1)
916+
full_coeff_model = td.VerticalNaturalConvectionCoeffModel(medium=air.heat, plate_length=1)
917+
sim = td.HeatChargeSimulation(
918+
size=(2, 2, 2),
919+
center=(0, 0, 0),
920+
medium=td.MultiPhysicsMedium(heat=td.FluidMedium()),
921+
structures=[solid_structure_l, fluid_structure_r],
922+
boundary_spec=[
923+
td.HeatBoundarySpec(
924+
placement=td.MediumMediumInterface(mediums=["air", "solid"]),
925+
condition=td.ConvectionBC(ambient_temperature=300, transfer_coeff=coeff_model),
926+
)
927+
],
928+
grid_spec=td.UniformUnstructuredGrid(dl=0.1),
929+
monitors=[
930+
td.TemperatureMonitor(
931+
center=(0, 0, 0),
932+
size=(td.inf, td.inf, td.inf),
933+
name="test_monitor",
934+
unstructured=True,
935+
)
936+
],
937+
)
938+
939+
# Test that the model can be placed on an interface defined by structures
940+
sim.updated_copy(
941+
boundary_spec=[
942+
td.HeatBoundarySpec(
943+
placement=td.StructureStructureInterface(structures=["solid_l", "fluid_r"]),
944+
condition=td.ConvectionBC(ambient_temperature=300, transfer_coeff=coeff_model),
945+
)
946+
],
947+
)
948+
949+
# Verify that placing the model on an interface between two solid media
950+
# correctly raises a validation error.
951+
with pytest.raises(pd.ValidationError):
952+
sim.updated_copy(
953+
structures=[solid_structure_l, solid_structure_r],
954+
boundary_spec=[
955+
td.HeatBoundarySpec(
956+
placement=td.StructureStructureInterface(structures=["solid_l", "solid_r"]),
957+
condition=td.ConvectionBC(ambient_temperature=300, transfer_coeff=coeff_model),
958+
)
959+
],
960+
)
961+
962+
# Verify that using a fluid medium with incomplete physical properties
963+
# for the natural convection calculation raises a validation error.
964+
incomplete_air = td.MultiPhysicsMedium(
965+
heat=td.FluidMedium(expansivity=1 / 300.0), name="incomplete_air"
966+
)
967+
with pytest.raises(pd.ValidationError):
968+
new_fluid_structure_r = fluid_structure_r.updated_copy(medium=incomplete_air)
969+
sim.updated_copy(
970+
structures=[solid_structure_l, new_fluid_structure_r],
971+
boundary_spec=[
972+
td.HeatBoundarySpec(
973+
placement=td.MediumMediumInterface(mediums=["incomplete_air", "solid"]),
974+
condition=td.ConvectionBC(ambient_temperature=300, transfer_coeff=coeff_model),
975+
)
976+
],
977+
)
978+
979+
# Test the case where the convection model has its own fluid medium explicitly defined.
980+
# The simulation should use the properties from the model's medium and ignore the
981+
# fluid present at the interface.
982+
sim.updated_copy(
983+
boundary_spec=[
984+
td.HeatBoundarySpec(
985+
placement=td.StructureStructureInterface(structures=["solid_l", "fluid_r"]),
986+
condition=td.ConvectionBC(ambient_temperature=300, transfer_coeff=full_coeff_model),
987+
)
988+
],
989+
)
990+
991+
# Verify that a validation error is raised if the medium supplied directly to the
992+
# coefficient model has incomplete properties for the natural convection calculation.
993+
incomplete_coeff_model = coeff_model.updated_copy(medium=incomplete_air.heat)
994+
with pytest.raises(pd.ValidationError):
995+
sim.updated_copy(
996+
boundary_spec=[
997+
td.HeatBoundarySpec(
998+
placement=td.MediumMediumInterface(mediums=["air", "solid"]),
999+
condition=td.ConvectionBC(
1000+
ambient_temperature=300, transfer_coeff=incomplete_coeff_model
1001+
),
1002+
),
1003+
]
1004+
)
1005+
1006+
8811007
def test_heat_charge_monitors_validation(monitors):
8821008
"""Checks for no name and negative size in monitors."""
8831009
temp_mnt = monitors[0]

tidy3d/components/material/tcad/heat.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class FluidMedium(AbstractHeatMedium):
6969
7070
Examples
7171
--------
72+
>>> # If you are using a boundary condition without a natural convection model,
73+
>>> # the specific properties of the fluid are not required. In this common
74+
>>> # scenario, you can instantiate the class without arguments.
75+
>>> air = FluidMedium()
76+
7277
>>> # It is most convenient to define the fluid from standard SI units
7378
>>> # using the `from_si_units` classmethod.
7479
>>> # The following defines air at approximately 20°C.
@@ -87,7 +92,7 @@ class FluidMedium(AbstractHeatMedium):
8792
... viscosity=1.81e-11,
8893
... specific_heat=1.005e+15,
8994
... density=1.204e-18,
90-
... expansivity=0.00341
95+
... expansivity=1/293.15
9196
... )
9297
"""
9398

tidy3d/components/tcad/boundary/heat.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ class VerticalNaturalConvectionCoeffModel(Tidy3dBaseModel):
6464
medium: FluidMedium = pd.Field(
6565
default=None,
6666
title="Interface medium",
67-
description="Medium to use for heat transfer coefficient.",
67+
description=(
68+
"The `FluidMedium` used for the heat transfer coefficient calculation. "
69+
"If `None`, the fluid is automatically deduced from the interface, which can be defined"
70+
"by either a `MediumMediumInterface` or a `StructureStructureInterface`."
71+
),
6872
)
6973

7074
plate_length: pd.NonNegativeFloat = pd.Field(
@@ -116,6 +120,33 @@ class ConvectionBC(HeatChargeBC):
116120
-------
117121
>>> import tidy3d as td
118122
>>> bc = td.ConvectionBC(ambient_temperature=300, transfer_coeff=1)
123+
124+
>>> # Convection with a natural convection model.
125+
>>> # First, define the fluid medium (e.g. air at 300 K).
126+
>>> air = td.FluidMedium.from_si_units(
127+
... thermal_conductivity=0.0257, # Unit: W/(m*K)
128+
... viscosity=1.81e-5, # Unit: Pa*s
129+
... specific_heat=1005, # Unit: J/(kg*K)
130+
... density=1.204, # Unit: kg/m^3
131+
... expansivity=1/293.15 # Unit: 1/K
132+
... )
133+
>>>
134+
>>> # Next, create the model, which requires the fluid and a characteristic length.
135+
>>> natural_conv_model = td.VerticalNaturalConvectionCoeffModel.from_si_units(
136+
... medium=air, characteristic_length=1e-5
137+
... )
138+
>>>
139+
>>> # Finally, create the boundary condition using this model.
140+
>>> bc_natural = td.ConvectionBC(
141+
... ambient_temperature=300, transfer_coeff=natural_conv_model
142+
... )
143+
144+
>>> # If the fluid medium is not provided to the coefficient model, it is automatically retrieved from
145+
>>> # the interface.
146+
>>> natural_conv_model_nom = td.VerticalNaturalConvectionCoeffModel.from_si_units(characteristic_length=1e-5)
147+
>>> bc_natural_nom = td.ConvectionBC(
148+
... ambient_temperature=300, transfer_coeff=natural_conv_model
149+
... )
119150
"""
120151

121152
ambient_temperature: pd.PositiveFloat = pd.Field(

tidy3d/components/tcad/simulation/heat_charge.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -530,14 +530,7 @@ def check_fluid_medium_attr(fluid_medium):
530530

531531
# Case 2: The fluid medium IS specified directly in the convection model.
532532
else:
533-
fluid_medium = natural_conv_model.medium
534-
if not isinstance(fluid_medium, FluidMedium):
535-
raise SetupError(
536-
f"Boundary spec at index {i}: The medium '{fluid_medium.name}' specified in "
537-
f"'VerticalNaturalConvectionCoeffModel' must be a fluid, but it has a heat "
538-
f"spec of type '{type(fluid_medium).__name__}'."
539-
)
540-
check_fluid_medium_attr(fluid_medium)
533+
check_fluid_medium_attr(natural_conv_model.medium)
541534
return values
542535

543536
@pd.validator("size", always=True)

0 commit comments

Comments
 (0)