Skip to content

Commit 35918db

Browse files
committed
Adding support for current density monitor
1 parent 90f9f8c commit 35918db

File tree

6 files changed

+177
-40
lines changed

6 files changed

+177
-40
lines changed

tests/test_components/test_heat_charge.py

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

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

259+
current_density_mnt = td.SteadyCurrentDensityMonitor(
260+
size=(1.6, 2, 3), name="electric_field_mnt"
261+
)
262+
259263
return [
260264
temp_mnt1, # 0
261265
temp_mnt2, # 1
@@ -270,6 +274,7 @@ def monitors():
270274
energy_band_mnt1, # 10
271275
mesh_mnt, # 11
272276
electric_field_mnt, # 12
277+
current_density_mnt, # 13
273278
]
274279

275280

@@ -755,6 +760,19 @@ def electric_field_monitor_data(monitors):
755760
return (mnt_data1, mnt_data2, mnt_data3)
756761

757762

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

930948

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

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

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

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

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

9791013

9801014
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,
@@ -657,6 +659,8 @@ def set_logging_level(level: str) -> None:
657659
"Staircasing",
658660
"SteadyCapacitanceData",
659661
"SteadyCapacitanceMonitor",
662+
"SteadyCurrentDensityData",
663+
"SteadyCurrentDensityMonitor",
660664
"SteadyElectricFieldData",
661665
"SteadyElectricFieldMonitor",
662666
"SteadyEnergyBandData",

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

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from tidy3d.components.tcad.data.monitor_data.abstract import HeatChargeMonitorData
2121
from tidy3d.components.tcad.monitors.charge import (
2222
SteadyCapacitanceMonitor,
23+
SteadyCurrentDensityMonitor,
2324
SteadyElectricFieldMonitor,
2425
SteadyEnergyBandMonitor,
2526
SteadyFreeCarrierMonitor,
@@ -467,7 +468,7 @@ def symmetry_expanded_copy(self) -> SteadyCapacitanceData:
467468

468469
class SteadyElectricFieldData(HeatChargeMonitorData):
469470
"""
470-
Stores electric field :math:`\\vec{E}` from a charge simulation.
471+
Stores electric field :math:`\\vec{E}` from a Charge/Conduction simulation.
471472
472473
Notes
473474
-----
@@ -478,7 +479,7 @@ class SteadyElectricFieldData(HeatChargeMonitorData):
478479
monitor: SteadyElectricFieldMonitor = pd.Field(
479480
...,
480481
title="Electric field monitor",
481-
description="Electric field data associated with a Charge simulation.",
482+
description="Electric field data associated with a Charge/Conduction simulation.",
482483
)
483484

484485
E: UnstructuredFieldType = pd.Field(
@@ -542,3 +543,78 @@ def field_name(self, val: str = "") -> str:
542543
return "E²"
543544
else:
544545
return "E"
546+
547+
548+
class SteadyCurrentDensityData(HeatChargeMonitorData):
549+
"""
550+
Stores current density :math:`\\vec{J}` from a Charge/Conduction simulation. It is given in
551+
units of :math:`A/\\mu m^2`
552+
"""
553+
554+
monitor: SteadyCurrentDensityMonitor = pd.Field(
555+
...,
556+
title="Current density monitor",
557+
description="Current density data associated with a Charge/Conduction simulation.",
558+
)
559+
560+
J: UnstructuredFieldType = pd.Field(
561+
None,
562+
title="Current density",
563+
description=r"Contains the computed current density in :math:`A/\\mu m^2`.",
564+
discriminator=TYPE_TAG_STR,
565+
)
566+
567+
@property
568+
def field_components(self) -> dict[str, UnstructuredFieldType]:
569+
"""Maps the field components to their associated data."""
570+
return {"J": self.J}
571+
572+
@pd.root_validator(skip_on_failure=True)
573+
def warn_no_data(cls, values):
574+
"""Warn if no data provided."""
575+
576+
mnt = values.get("monitor")
577+
J = values.get("J")
578+
579+
if J is None:
580+
log.warning(
581+
f"No data is available for monitor '{mnt.name}'. This is typically caused by "
582+
"monitor not intersecting any solid medium."
583+
)
584+
585+
return values
586+
587+
@pd.root_validator(skip_on_failure=True)
588+
def check_correct_data_type(cls, values):
589+
"""Issue error if incorrect data type is used"""
590+
591+
mnt = values.get("monitor")
592+
J = values.get("J")
593+
594+
if isinstance(J, TetrahedralGridDataset) or isinstance(J, TriangularGridDataset):
595+
AcceptedTypes = (IndexedFieldVoltageDataArray, PointDataArray)
596+
if not isinstance(J.values, AcceptedTypes):
597+
raise ValueError(
598+
f"In the data associated with monitor {mnt}, must contain a field. This can be "
599+
"defined with IndexedFieldVoltageDataArray or PointDataArray."
600+
)
601+
602+
return values
603+
604+
@property
605+
def symmetry_expanded_copy(self) -> SteadyCurrentDensityData:
606+
"""Return copy of self with symmetry applied."""
607+
608+
new_J = self._symmetry_expanded_copy(property=self.J)
609+
610+
return self.updated_copy(
611+
J=new_J,
612+
symmetry=(0, 0, 0),
613+
)
614+
615+
def field_name(self, val: str = "") -> str:
616+
"""Gets the name of the fields to be plotted."""
617+
if val == "abs^2":
618+
return "J²"
619+
else:
620+
return "J"

tidy3d/components/tcad/data/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from tidy3d.components.tcad.data.monitor_data.charge import (
88
SteadyCapacitanceData,
9+
SteadyCurrentDensityData,
910
SteadyElectricFieldData,
1011
SteadyEnergyBandData,
1112
SteadyFreeCarrierData,
@@ -20,4 +21,5 @@
2021
SteadyElectricFieldData,
2122
SteadyEnergyBandData,
2223
SteadyCapacitanceData,
24+
SteadyCurrentDensityData,
2325
]

tidy3d/components/tcad/monitors/charge.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class SteadyCapacitanceMonitor(HeatChargeMonitor):
8484

8585
class SteadyElectricFieldMonitor(HeatChargeMonitor):
8686
"""
87-
Electric field monitor for Charge simulations.
87+
Electric field monitor for Charge/Conduction simulations.
8888
8989
Example
9090
-------
@@ -99,3 +99,22 @@ class SteadyElectricFieldMonitor(HeatChargeMonitor):
9999
title="Unstructured Grid",
100100
description="Return data on the original unstructured grid.",
101101
)
102+
103+
104+
class SteadyCurrentDensityMonitor(HeatChargeMonitor):
105+
"""
106+
Current density monitor for Charge/Conduction simulations.
107+
108+
Example
109+
-------
110+
>>> import tidy3d as td
111+
>>> current_density_monitor_z0 = td.SteadyCurrentDensityMonitor(
112+
... center=(0, 0.14, 0), size=(0.6, 0.3, 0), name="current_density_z0",
113+
... )
114+
"""
115+
116+
unstructured: Literal[True] = pd.Field(
117+
True,
118+
title="Unstructured Grid",
119+
description="Return data on the original unstructured grid.",
120+
)

tidy3d/components/tcad/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from tidy3d.components.tcad.mobility import CaugheyThomasMobility, ConstantMobilityModel
1414
from tidy3d.components.tcad.monitors.charge import (
1515
SteadyCapacitanceMonitor,
16+
SteadyCurrentDensityMonitor,
1617
SteadyElectricFieldMonitor,
1718
SteadyEnergyBandMonitor,
1819
SteadyFreeCarrierMonitor,
@@ -37,6 +38,7 @@
3738
SteadyEnergyBandMonitor,
3839
SteadyElectricFieldMonitor,
3940
SteadyCapacitanceMonitor,
41+
SteadyCurrentDensityMonitor,
4042
]
4143
HeatChargeSourceType = Union[HeatSource, HeatFromElectricSource, UniformHeatSource]
4244
HeatChargeBCType = Union[

0 commit comments

Comments
 (0)