Skip to content

Commit d325a9b

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

File tree

7 files changed

+180
-41
lines changed

7 files changed

+180
-41
lines changed

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/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/simulation/heat_charge.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
)
5050
from tidy3d.components.tcad.monitors.charge import (
5151
SteadyCapacitanceMonitor,
52+
SteadyCurrentDensityMonitor,
5253
SteadyFreeCarrierMonitor,
5354
SteadyPotentialMonitor,
5455
)
@@ -546,6 +547,7 @@ def check_charge_simulation(cls, values):
546547
SteadyPotentialMonitor,
547548
SteadyFreeCarrierMonitor,
548549
SteadyCapacitanceMonitor,
550+
SteadyCurrentDensityMonitor,
549551
)
550552

551553
simulation_types = cls._check_simulation_types(values=values)
@@ -568,7 +570,7 @@ def check_charge_simulation(cls, values):
568570
if not any(isinstance(mnt, ChargeMonitorType) for mnt in monitors):
569571
raise SetupError(
570572
"Charge simulations require the definition of, at least, one of these monitors: "
571-
"'[SteadyPotentialMonitor, SteadyFreeCarrierMonitor, SteadyCapacitanceMonitor]' "
573+
"'[SteadyPotentialMonitor, SteadyFreeCarrierMonitor, SteadyCapacitanceMonitor, SteadyCurrentDensityMonitor]' "
572574
"but none have been defined."
573575
)
574576

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)