Skip to content

Commit 8e9dcdc

Browse files
committed
Adding support for current density monitor
1 parent fba5441 commit 8e9dcdc

File tree

13 files changed

+676
-251
lines changed

13 files changed

+676
-251
lines changed

schemas/HeatChargeSimulation.json

Lines changed: 164 additions & 3 deletions
Large diffs are not rendered by default.

schemas/HeatSimulation.json

Lines changed: 164 additions & 3 deletions
Large diffs are not rendered by default.

schemas/TerminalComponentModeler.json

Lines changed: 170 additions & 5 deletions
Large diffs are not rendered by default.

tests/test_components/test_heat_charge.py

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ def monitors():
257257

258258
electric_field_mnt = td.SteadyElectricFieldMonitor(size=(1.6, 2, 3), name="electric_field_test")
259259

260+
current_density_mnt = td.SteadyCurrentDensityMonitor(
261+
size=(1.6, 2, 3), name="current_density_mnt"
262+
)
263+
260264
return [
261265
temp_mnt1, # 0
262266
temp_mnt2, # 1
@@ -271,6 +275,7 @@ def monitors():
271275
energy_band_mnt1, # 10
272276
mesh_mnt, # 11
273277
electric_field_mnt, # 12
278+
current_density_mnt, # 13
274279
]
275280

276281

@@ -756,6 +761,19 @@ def electric_field_monitor_data(monitors):
756761
return (mnt_data1, mnt_data2, mnt_data3)
757762

758763

764+
@pytest.fixture(scope="module")
765+
def current_density_monitor_data(monitors, electric_field_monitor_data):
766+
"""Creates different current density monitor data."""
767+
monitor = monitors[13]
768+
e_data1, e_data2, e_data3 = electric_field_monitor_data
769+
770+
mnt_data1 = td.SteadyCurrentDensityData(monitor=monitor, J=e_data1.E)
771+
mnt_data2 = td.SteadyCurrentDensityData(monitor=monitor, J=e_data2.E)
772+
mnt_data3 = td.SteadyCurrentDensityData(monitor=monitor, J=e_data3.E)
773+
774+
return (mnt_data1, mnt_data2, mnt_data3)
775+
776+
759777
@pytest.fixture(scope="module")
760778
def simulation_data(
761779
heat_simulation,
@@ -930,52 +948,68 @@ def test_monitor_crosses_medium(mediums, structures, heat_simulation, conduction
930948

931949

932950
def test_heat_charge_mnt_data(
933-
temperature_monitor_data, voltage_monitor_data, electric_field_monitor_data
951+
temperature_monitor_data,
952+
voltage_monitor_data,
953+
electric_field_monitor_data,
954+
current_density_monitor_data,
934955
):
935956
"""Tests whether different heat-charge monitor data can be created."""
936957
assert len(temperature_monitor_data) == 4, "Expected 4 temperature monitor data entries."
937958
assert len(voltage_monitor_data) == 4, "Expected 4 voltage monitor data entries."
938959
assert len(electric_field_monitor_data) == 3, "Expected 3 electric field monitor data entries."
960+
assert len(current_density_monitor_data) == 3, (
961+
"Expected 3 current density monitor data entries."
962+
)
963+
964+
for var, mnt_data_lists in [
965+
("E", electric_field_monitor_data),
966+
("J", current_density_monitor_data),
967+
]:
968+
for mnt_data in mnt_data_lists:
969+
assert var in mnt_data.field_components.keys()
970+
971+
symm_data = mnt_data.symmetry_expanded_copy
972+
if var == "E":
973+
assert symm_data.E == mnt_data.E
974+
elif var == "J":
975+
assert symm_data.J == mnt_data.J
976+
977+
names = mnt_data.field_name("abs^2")
978+
assert names == var + "²"
979+
names = mnt_data.field_name()
980+
assert names == var
981+
982+
# make sure an error is raised if we don't use a field data array
983+
# TriangularGridDataset
984+
tri_grid_points = td.PointDataArray(
985+
[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]],
986+
dims=("index", "axis"),
987+
)
939988

940-
for mnt_data in electric_field_monitor_data:
941-
assert "E" in mnt_data.field_components.keys()
942-
943-
symm_data = mnt_data.symmetry_expanded_copy
944-
assert symm_data.E == mnt_data.E
945-
946-
names = mnt_data.field_name("abs^2")
947-
assert names == "E²"
948-
names = mnt_data.field_name()
949-
assert names == "E"
950-
951-
# make sure an error is raised if we don't use a field data array
952-
# TriangularGridDataset
953-
tri_grid_points = td.PointDataArray(
954-
[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]],
955-
dims=("index", "axis"),
956-
)
957-
958-
tri_grid_cells = td.CellDataArray(
959-
[[0, 1, 2], [1, 2, 3]],
960-
dims=("cell_index", "vertex_index"),
961-
)
989+
tri_grid_cells = td.CellDataArray(
990+
[[0, 1, 2], [1, 2, 3]],
991+
dims=("cell_index", "vertex_index"),
992+
)
962993

963-
tri_grid_values = td.IndexedDataArray(
964-
[1.0, 2.0, 3.0, 4.0],
965-
dims=("index",),
966-
name="T",
967-
)
994+
tri_grid_values = td.IndexedDataArray(
995+
[1.0, 2.0, 3.0, 4.0],
996+
dims=("index",),
997+
name="T",
998+
)
968999

969-
tri_grid = td.TriangularGridDataset(
970-
normal_axis=1,
971-
normal_pos=0,
972-
points=tri_grid_points,
973-
cells=tri_grid_cells,
974-
values=tri_grid_values,
975-
)
1000+
tri_grid = td.TriangularGridDataset(
1001+
normal_axis=1,
1002+
normal_pos=0,
1003+
points=tri_grid_points,
1004+
cells=tri_grid_cells,
1005+
values=tri_grid_values,
1006+
)
9761007

977-
with pytest.raises(pd.ValidationError):
978-
_ = mnt_data.updated_copy(E=tri_grid)
1008+
with pytest.raises(pd.ValidationError):
1009+
if var == "E":
1010+
_ = mnt_data.updated_copy(E=tri_grid)
1011+
elif var == "J":
1012+
_ = mnt_data.updated_copy(J=tri_grid)
9791013

9801014

9811015
def test_grid_spec_validation(grid_specs):

tidy3d/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
)
3838
from tidy3d.components.tcad.data.types import (
3939
SteadyCapacitanceData,
40+
SteadyCurrentDensityData,
4041
SteadyElectricFieldData,
4142
SteadyEnergyBandData,
4243
SteadyFreeCarrierData,
@@ -54,6 +55,7 @@
5455
from tidy3d.components.tcad.mesher import VolumeMesher
5556
from tidy3d.components.tcad.monitors.charge import (
5657
SteadyCapacitanceMonitor,
58+
SteadyCurrentDensityMonitor,
5759
SteadyElectricFieldMonitor,
5860
SteadyEnergyBandMonitor,
5961
SteadyFreeCarrierMonitor,
@@ -673,6 +675,8 @@ def set_logging_level(level: str) -> None:
673675
"Staircasing",
674676
"SteadyCapacitanceData",
675677
"SteadyCapacitanceMonitor",
678+
"SteadyCurrentDensityData",
679+
"SteadyCurrentDensityMonitor",
676680
"SteadyElectricFieldData",
677681
"SteadyElectricFieldMonitor",
678682
"SteadyEnergyBandData",

tidy3d/components/tcad/data/monitor_data/abstract.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
HeatChargeMonitorType,
1919
)
2020
from tidy3d.components.types import Coordinate, ScalarSymmetry, annotate_type
21+
from tidy3d.log import log
2122

2223
FieldDataset = Union[
2324
SpatialDataArray, annotate_type(Union[TriangularGridDataset, TetrahedralGridDataset])
@@ -47,15 +48,31 @@ class HeatChargeMonitorData(AbstractMonitorData, ABC):
4748
)
4849

4950
@abstractmethod
51+
def field_components(self) -> dict:
52+
"""Maps the field components to their associated data."""
53+
54+
def field_name(self, val: str = "") -> str:
55+
"""Gets the name of the fields to be plot."""
56+
fields = self.field_components.keys()
57+
name = ""
58+
for field in fields:
59+
if val == "abs^2":
60+
name = f"{field}²"
61+
else:
62+
name = f"{field}"
63+
return name
64+
65+
@property
5066
def symmetry_expanded_copy(self) -> HeatChargeMonitorData:
5167
"""Return copy of self with symmetry applied."""
5268

53-
@abstractmethod
54-
def field_name(self, val: str) -> str:
55-
"""Gets the name of the fields to be plot."""
69+
new_field_components = {}
70+
for field, val in self.field_components.items():
71+
new_field_components[field] = self._symmetry_expanded_copy_base(property=val)
5672

57-
# def _symmetry_expanded_copy(self, property):
58-
def _symmetry_expanded_copy(self, property: FieldDataset) -> FieldDataset:
73+
return self.updated_copy(symmetry=(0, 0, 0), **new_field_components)
74+
75+
def _symmetry_expanded_copy_base(self, property: FieldDataset) -> FieldDataset:
5976
"""Return the property with symmetry applied."""
6077

6178
# no symmetry
@@ -118,3 +135,13 @@ def _symmetry_expanded_copy(self, property: FieldDataset) -> FieldDataset:
118135
new_property = new_property.box_clip(bounds=clip_bounds)
119136

120137
return new_property
138+
139+
def _post_init_validators(self):
140+
"""Call validators taking ``self`` that get run after init."""
141+
# validate that data exists for all fields
142+
for field_name, field in self.field_components.items():
143+
if field is None:
144+
log.warning(
145+
f"No data is available for monitor '{self.monitor.name}' field '{field_name}'. "
146+
"This is typically caused by monitor not intersecting any solid medium."
147+
)

0 commit comments

Comments
 (0)