Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Added `symmetrize_mirror`, `symmetrize_rotation`, `symmetrize_diagonal` functions to the autograd plugin. They can be used for enforcing symmetries in topology optimization.
- Added property `charge` to the `plot_property` of `HeatChargeSimulation`. This allows to visualize charge simulations with its BCs.

### Changed
- Removed validator that would warn if `PerturbationMedium` values could become numerically unstable, since an error will anyway be raised if this actually happens when the medium is converted using actual perturbation data.
Expand Down
39 changes: 33 additions & 6 deletions tests/test_components/test_heat_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ class CHARGE_SIMULATION:
# --------------------------


@pytest.fixture(scope="module")
def charge_tolerance():
"""Charge tolerance settings for simulations."""
return td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400)


@pytest.fixture(scope="module")
def mediums():
"""Creates mediums with different specifications."""
Expand Down Expand Up @@ -385,7 +391,7 @@ def voltage_capacitance_simulation(mediums, structures, boundary_conditions, mon
condition=td.VoltageBC(source=td.DCVoltageSource(voltage=0)),
)

# Lets pick a couple of monitors. We'll definitely include the CapacitanceMonitor
# Let's pick a couple of monitors. We'll definitely include the CapacitanceMonitor
# (monitors[8] -> 'cap_mt1') so that we can measure capacitance. We can also include
# a potential monitor to see the fields, e.g. monitors[4] -> volt_mnt1 for demonstration.
cap_monitor = monitors[8] # 'capacitance_mnt1'
Expand Down Expand Up @@ -1527,11 +1533,6 @@ def capacitance_global_mnt(self):
unstructured=True,
)

# Define charge settings as fixtures within the class
@pytest.fixture(scope="class")
def charge_tolerance(self):
return td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400)

def test_charge_simulation(
self,
Si_n,
Expand Down Expand Up @@ -2513,3 +2514,29 @@ def test_generation_recombination():
beta_n=1,
beta_p=1,
)


def test_plot_property_charge(heat_simulation, conduction_simulation, charge_tolerance):
"""Test plot_property with property="charge"."""

# transform the conduction simulation into a charge simulation
new_structs = []
for struct in conduction_simulation.structures:
new_medium = CHARGE_SIMULATION.intrinsic_Si.updated_copy(name=struct.medium.name)
new_structs.append(struct.updated_copy(medium=new_medium))
analysis_spec = td.IsothermalSteadyChargeDCAnalysis(
temperature=300,
convergence_dv=0.1,
tolerance_settings=charge_tolerance,
)
charge_simulation = conduction_simulation.updated_copy(
structures=new_structs,
analysis_spec=analysis_spec,
validate=False,
)
charge_simulation.plot_property(property="charge", z=0)

with pytest.raises(ValueError):
heat_simulation.plot_property(property="charge", z=0)
with pytest.raises(ValueError):
conduction_simulation.plot_property(property="charge", z=0)
52 changes: 37 additions & 15 deletions tidy3d/components/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,12 +564,14 @@ def _plot_shape_structure(
shape: Shapely,
ax: Ax,
fill: bool = True,
property: str = "heat_conductivity",
) -> Ax:
"""Plot a structure's cross section shape for a given medium."""
plot_params_struct = self._get_structure_plot_params(
medium=medium,
mat_index=mat_index,
fill=fill,
property=property,
)
ax = self.box.plot_shape(shape=shape, plot_params=plot_params_struct, ax=ax)
return ax
Expand All @@ -579,6 +581,7 @@ def _get_structure_plot_params(
mat_index: int,
medium: MultiPhysicsMediumType3D,
fill: bool = True,
property: str = "heat_conductivity",
) -> PlotParams:
"""Constructs the plot parameters for a given medium in scene.plot()."""

Expand Down Expand Up @@ -629,6 +632,9 @@ def _get_structure_plot_params(
if medium.viz_spec is not None:
plot_params = plot_params.override_with_viz_spec(medium.viz_spec)

if property == "charge":
plot_params = plot_params.copy(update={"edgecolor": "k", "linewidth": 1})

if not fill:
plot_params = plot_params.copy(update={"fill": False})
if plot_params.linewidth == 0:
Expand Down Expand Up @@ -1496,7 +1502,9 @@ def plot_heat_charge_property(
z: Optional[float] = None,
alpha: Optional[float] = None,
cbar: bool = True,
property: str = "heat_conductivity",
property: Literal[
"heat_conductivity", "electric_conductivity", "charge"
] = "heat_conductivity",
ax: Ax = None,
hlim: Optional[tuple[float, float]] = None,
vlim: Optional[tuple[float, float]] = None,
Expand All @@ -1517,9 +1525,9 @@ def plot_heat_charge_property(
Defaults to the structure default alpha.
cbar : bool = True
Whether to plot a colorbar for the thermal conductivity.
property : str = "heat_conductivity"
The heat-charge siimulation property to plot. The options are
["heat_conductivity", "electric_conductivity"]
property : Literal["heat_conductivity", "electric_conductivity", "charge"] = "heat_conductivity"
The heat-charge simulation property to plot. The options are
["heat_conductivity", "electric_conductivity", "charge"]
ax : matplotlib.axes._subplots.Axes = None
Matplotlib axes to plot on, if not specified, one is created.
hlim : Tuple[float, float] = None
Expand Down Expand Up @@ -1615,7 +1623,9 @@ def plot_structures_heat_charge_property(
z: Optional[float] = None,
alpha: Optional[float] = None,
cbar: bool = True,
property: str = "heat_conductivity",
property: Literal[
"heat_conductivity", "electric_conductivity", "charge"
] = "heat_conductivity",
reverse: bool = False,
ax: Ax = None,
hlim: Optional[tuple[float, float]] = None,
Expand Down Expand Up @@ -1676,16 +1686,26 @@ def plot_structures_heat_charge_property(

property_val_min, property_val_max = self.heat_charge_property_bounds(property=property)
for medium, shape in medium_shapes:
ax = self._plot_shape_structure_heat_charge_property(
alpha=alpha,
medium=medium,
property_val_min=property_val_min,
property_val_max=property_val_max,
reverse=reverse,
shape=shape,
ax=ax,
property=property,
)
if property == "charge":
ax = self._plot_shape_structure(
medium=medium,
mat_index=self.medium_map[medium],
shape=shape,
ax=ax,
fill=True,
property=property,
)
else:
ax = self._plot_shape_structure_heat_charge_property(
alpha=alpha,
medium=medium,
property_val_min=property_val_min,
property_val_max=property_val_max,
reverse=reverse,
shape=shape,
ax=ax,
property=property,
)

if cbar:
label = ""
Expand Down Expand Up @@ -1732,6 +1752,8 @@ def heat_charge_property_bounds(self, property) -> tuple[float, float]:
medium for medium in medium_list if isinstance(medium.charge, ChargeConductorMedium)
]
cond_list = [medium.charge.conductivity for medium in cond_mediums]
elif property == "charge":
return 0, 1 # Return a default range for 'charge' property

if len(cond_list) == 0:
cond_list = [0]
Expand Down
21 changes: 15 additions & 6 deletions tidy3d/components/tcad/simulation/heat_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ def plot_property(
Opacity of the monitors. If ``None``, uses Tidy3d default.
property : str = "heat_conductivity"
Specified the type of simulation for which the plot will be tailored.
Options are ["heat_conductivity", "electric_conductivity", "source"]
Options are ["heat_conductivity", "electric_conductivity", "source", "charge"]
hlim : Tuple[float, float] = None
The x range if plotting on xy or xz planes, y range if plotting on yz plane.
vlim : Tuple[float, float] = None
Expand All @@ -1204,6 +1204,8 @@ def plot_property(
)

cbar_cond = True
if property == "charge":
cbar_cond = False

simulation_types = self._get_simulation_types()
if property == "source" and len(simulation_types) > 1:
Expand All @@ -1215,9 +1217,15 @@ def plot_property(
)
if len(simulation_types) == 1:
if (
property == "heat_conductivity" and TCADAnalysisTypes.CONDUCTION in simulation_types
) or (
property == "electric_conductivity" and TCADAnalysisTypes.HEAT in simulation_types
(
property == "heat_conductivity"
and TCADAnalysisTypes.CONDUCTION in simulation_types
)
or (
property == "electric_conductivity"
and TCADAnalysisTypes.HEAT in simulation_types
)
or (property == "charge" and TCADAnalysisTypes.CHARGE not in simulation_types)
):
raise ValueError(
f"'property' in 'plot_property()' was defined as {property} but the "
Expand Down Expand Up @@ -1378,7 +1386,7 @@ def plot_boundaries(
# plot boundary conditions
if property == "heat_conductivity" or property == "source":
new_boundaries = [(b, s) for b, s in boundaries if isinstance(b.condition, HeatBCTypes)]
elif property == "electric_conductivity":
elif property == "electric_conductivity" or property == "charge":
new_boundaries = [
(b, s) for b, s in boundaries if isinstance(b.condition, ElectricBCTypes)
]
Expand Down Expand Up @@ -1762,7 +1770,7 @@ def plot_sources(
# get appropriate sources
if property == "heat_conductivity" or property == "source":
source_list = [s for s in self.sources if isinstance(s, HeatSourceTypes)]
elif property == "electric_conductivity":
elif property == "electric_conductivity" or property == "charge":
source_list = [s for s in self.sources if isinstance(s, ChargeSourceTypes)]

# distribute source where there are assigned
Expand Down Expand Up @@ -1818,6 +1826,7 @@ def _add_source_cbar(self, ax: Ax, property: str = "heat_conductivity") -> None:
def source_bounds(self, property: str = "heat_conductivity") -> tuple[float, float]:
"""Compute range of heat sources present in the simulation."""

rate_list = []
if property == "heat_conductivity" or property == "source":
rate_list = [
np.mean(source.rate) for source in self.sources if isinstance(source, HeatSource)
Expand Down