Skip to content

Commit 8ff1ec8

Browse files
committed
Front end classes
Monitor tests
1 parent 7c093b1 commit 8ff1ec8

File tree

7 files changed

+235
-13
lines changed

7 files changed

+235
-13
lines changed

tests/test_components/test_heat_charge.py

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -252,18 +252,21 @@ def monitors():
252252

253253
energy_band_mnt1 = td.SteadyEnergyBandMonitor(size=(1.6, 2, 3), name="bandgap_test")
254254

255+
electric_field_mnt = td.SteadyElectricFieldMonitor(size=(1.6, 2, 3), name="electric_field_test")
256+
255257
return [
256-
temp_mnt1,
257-
temp_mnt2,
258-
temp_mnt3,
259-
temp_mnt4,
260-
volt_mnt1,
261-
volt_mnt2,
262-
volt_mnt3,
263-
volt_mnt4,
264-
capacitance_mnt1,
265-
free_carrier_mnt1,
266-
energy_band_mnt1,
258+
temp_mnt1, # 0
259+
temp_mnt2, # 1
260+
temp_mnt3, # 2
261+
temp_mnt4, # 3
262+
volt_mnt1, # 4
263+
volt_mnt2, # 5
264+
volt_mnt3, # 6
265+
volt_mnt4, # 7
266+
capacitance_mnt1, # 8
267+
free_carrier_mnt1, # 9
268+
energy_band_mnt1, # 10
269+
electric_field_mnt, # 11
267270
]
268271

269272

@@ -516,7 +519,10 @@ def temperature_monitor_data(monitors):
516519
@pytest.fixture(scope="module")
517520
def voltage_monitor_data(monitors):
518521
"""Creates different voltage monitor data."""
519-
_, _, _, _, volt_mnt1, volt_mnt2, volt_mnt3, volt_mnt4, _, _, _ = monitors
522+
volt_mnt1 = monitors[4]
523+
volt_mnt2 = monitors[5]
524+
volt_mnt3 = monitors[6]
525+
volt_mnt4 = monitors[7]
520526

521527
# SpatialDataArray
522528
nx, ny, nz = 9, 6, 5
@@ -642,6 +648,84 @@ def energy_band_monitor_data(monitors):
642648
return (eb_data1,)
643649

644650

651+
@pytest.fixture(scope="module")
652+
def electric_field_monitor_data(monitors):
653+
"""Creates different electric field monitor data."""
654+
monitor = monitors[11]
655+
656+
# TetrahedralGridDataset
657+
tet_grid_points = td.PointDataArray(
658+
[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
659+
dims=("index", "axis"),
660+
)
661+
662+
tet_grid_cells = td.CellDataArray(
663+
[[0, 1, 2, 4], [1, 2, 3, 4]],
664+
dims=("cell_index", "vertex_index"),
665+
)
666+
667+
tet_grid_values = td.IndexedDataArray(
668+
[1.0, 2.0, 3.0, 4.0, 5.0],
669+
dims=("index",),
670+
name="T",
671+
)
672+
673+
tet_grid = td.TetrahedralGridDataset(
674+
points=tet_grid_points,
675+
cells=tet_grid_cells,
676+
values=tet_grid_values,
677+
)
678+
679+
mnt_data1 = td.SteadyElectricFieldData(monitor=monitor, Ex=tet_grid, Ey=tet_grid, Ez=tet_grid)
680+
681+
# TriangularGridDataset
682+
tri_grid_points = td.PointDataArray(
683+
[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]],
684+
dims=("index", "axis"),
685+
)
686+
687+
tri_grid_cells = td.CellDataArray(
688+
[[0, 1, 2], [1, 2, 3]],
689+
dims=("cell_index", "vertex_index"),
690+
)
691+
692+
tri_grid_values = td.IndexedDataArray(
693+
[1.0, 2.0, 3.0, 4.0],
694+
dims=("index",),
695+
name="T",
696+
)
697+
698+
tri_grid = td.TriangularGridDataset(
699+
normal_axis=1,
700+
normal_pos=0,
701+
points=tri_grid_points,
702+
cells=tri_grid_cells,
703+
values=tri_grid_values,
704+
)
705+
706+
mnt_data2 = td.SteadyElectricFieldData(monitor=monitor, Ex=tri_grid, Ey=tri_grid, Ez=tri_grid)
707+
708+
mnt_data3 = td.SteadyElectricFieldData(monitor=monitor, Ex=None, Ey=None, Ez=None)
709+
710+
data_v = td.IndexedVoltageDataArray(
711+
[[0.0, 1.5], [1.5, 2], [2.5, 3.0], [3.5, -4.0]],
712+
coords={"index": np.arange(4), "voltage": [-1, 1]},
713+
name="test",
714+
)
715+
tri_grid_v = td.TriangularGridDataset(
716+
normal_axis=1,
717+
normal_pos=0,
718+
points=tri_grid_points,
719+
cells=tri_grid_cells,
720+
values=data_v,
721+
)
722+
mnt_data4 = td.SteadyElectricFieldData(
723+
monitor=monitor, Ex=tri_grid_v, Ey=tri_grid_v, Ez=tri_grid_v
724+
)
725+
726+
return (mnt_data1, mnt_data2, mnt_data3, mnt_data4)
727+
728+
645729
@pytest.fixture(scope="module")
646730
def simulation_data(
647731
heat_simulation,
@@ -797,11 +881,28 @@ def test_monitor_crosses_medium(mediums, structures, heat_simulation, conduction
797881

798882

799883
def test_heat_charge_mnt_data(
800-
temperature_monitor_data, voltage_monitor_data, capacitance_monitor_data
884+
temperature_monitor_data, voltage_monitor_data, electric_field_monitor_data
801885
):
802886
"""Tests whether different heat-charge monitor data can be created."""
803887
assert len(temperature_monitor_data) == 4, "Expected 4 temperature monitor data entries."
804888
assert len(voltage_monitor_data) == 4, "Expected 4 voltage monitor data entries."
889+
assert len(electric_field_monitor_data) == 4, "Expected 4 electric field monitor data entries."
890+
891+
for mnt_data in electric_field_monitor_data:
892+
assert "Ex" in mnt_data.field_components.keys()
893+
assert "Ey" in mnt_data.field_components.keys()
894+
assert "Ez" in mnt_data.field_components.keys()
895+
896+
symm_data = mnt_data.symmetry_expanded_copy
897+
assert symm_data.Ex == mnt_data.Ex
898+
assert symm_data.Ey == mnt_data.Ey
899+
assert symm_data.Ez == mnt_data.Ez
900+
901+
names = mnt_data.field_name("abs^2")
902+
print(names)
903+
assert names == "Ex², Ey², Ez²"
904+
names = mnt_data.field_name()
905+
assert names == "Ex, Ey, Ez"
805906

806907

807908
def test_grid_spec_validation(grid_specs):

tidy3d/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
)
3636
from tidy3d.components.tcad.data.types import (
3737
SteadyCapacitanceData,
38+
SteadyElectricFieldData,
3839
SteadyEnergyBandData,
3940
SteadyFreeCarrierData,
4041
SteadyPotentialData,
@@ -50,6 +51,7 @@
5051
)
5152
from tidy3d.components.tcad.monitors.charge import (
5253
SteadyCapacitanceMonitor,
54+
SteadyElectricFieldMonitor,
5355
SteadyEnergyBandMonitor,
5456
SteadyFreeCarrierMonitor,
5557
SteadyPotentialMonitor,
@@ -651,6 +653,8 @@ def set_logging_level(level: str) -> None:
651653
"Staircasing",
652654
"SteadyCapacitanceData",
653655
"SteadyCapacitanceMonitor",
656+
"SteadyElectricFieldData",
657+
"SteadyElectricFieldMonitor",
654658
"SteadyEnergyBandData",
655659
"SteadyEnergyBandMonitor",
656660
"SteadyFreeCarrierData",

tidy3d/components/data/unstructured/base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,20 @@ def to_vtu(self, fname: str):
566566
writer.SetInputData(self._vtk_obj)
567567
writer.Write()
568568

569+
@classmethod
570+
@requires_vtk
571+
def _cell_to_point_data(
572+
cls,
573+
vtk_obj,
574+
):
575+
"""Get point data values from a VTK object."""
576+
577+
cellDataToPointData = vtk["mod"].vtkCellDataToPointData()
578+
cellDataToPointData.SetInputData(vtk_obj)
579+
cellDataToPointData.Update()
580+
581+
return cellDataToPointData.GetOutput()
582+
569583
@classmethod
570584
@requires_vtk
571585
def _get_values_from_vtk(

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from tidy3d.components.tcad.data.monitor_data.abstract import HeatChargeMonitorData
1919
from tidy3d.components.tcad.monitors.charge import (
2020
SteadyCapacitanceMonitor,
21+
SteadyElectricFieldMonitor,
2122
SteadyEnergyBandMonitor,
2223
SteadyFreeCarrierMonitor,
2324
SteadyPotentialMonitor,
@@ -460,3 +461,82 @@ def symmetry_expanded_copy(self) -> SteadyCapacitanceData:
460461
electron_capacitance=new_electron_capacitance,
461462
symmetry=(0, 0, 0),
462463
)
464+
465+
466+
class SteadyElectricFieldData(HeatChargeMonitorData):
467+
"""
468+
Stores electric field :math:`\\vec{E}` from a charge simulation.
469+
470+
Notes
471+
-----
472+
The electric field is computed as the negative gradient of the electric potential :math:`\\vec{E} = -\\nabla \\psi`.
473+
It is given in units of :math:`V/\\mu m` (Volts per micrometer).
474+
"""
475+
476+
monitor: SteadyElectricFieldMonitor = pd.Field(
477+
...,
478+
title="Electric field monitor",
479+
description="Electric field data associated with a Charge simulation.",
480+
)
481+
482+
Ex: UnstructuredFieldType = pd.Field(
483+
None,
484+
title="x component of the electric field",
485+
description=r"Contains the computed x component of the electric field in :math:`V/\\mu m`.",
486+
)
487+
488+
Ey: UnstructuredFieldType = pd.Field(
489+
None,
490+
title="y component of the electric field",
491+
description=r"Contains the computed y component of the electric field in :math:`V/\\mu m`.",
492+
)
493+
494+
Ez: UnstructuredFieldType = pd.Field(
495+
None,
496+
title="z component of the electric field",
497+
description=r"Contains the computed z component of the electric field in :math:`V/\\mu m`.",
498+
)
499+
500+
@property
501+
def field_components(self) -> dict[str, UnstructuredFieldType]:
502+
"""Maps the field components to their associated data."""
503+
return {"Ex": self.Ex, "Ey": self.Ey, "Ez": self.Ez}
504+
505+
@pd.root_validator(skip_on_failure=True)
506+
def warn_no_data(cls, values):
507+
"""Warn if no data provided."""
508+
509+
mnt = values.get("monitor")
510+
Ex = values.get("Ex")
511+
Ey = values.get("Ey")
512+
Ez = values.get("Ez")
513+
514+
if Ex is None or Ey is None or Ez is None:
515+
log.warning(
516+
f"No data is available for monitor '{mnt.name}'. This is typically caused by "
517+
"monitor not intersecting any solid medium."
518+
)
519+
520+
return values
521+
522+
@property
523+
def symmetry_expanded_copy(self) -> SteadyElectricFieldData:
524+
"""Return copy of self with symmetry applied."""
525+
526+
new_Ex = self._symmetry_expanded_copy(property=self.Ex)
527+
new_Ey = self._symmetry_expanded_copy(property=self.Ey)
528+
new_Ez = self._symmetry_expanded_copy(property=self.Ez)
529+
530+
return self.updated_copy(
531+
Ex=new_Ex,
532+
Ey=new_Ey,
533+
Ez=new_Ez,
534+
symmetry=(0, 0, 0),
535+
)
536+
537+
def field_name(self, val: str = "") -> str:
538+
"""Gets the name of the fields to be plotted."""
539+
if val == "abs^2":
540+
return "Ex², Ey², Ez²"
541+
else:
542+
return "Ex, Ey, Ez"

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+
SteadyElectricFieldData,
910
SteadyEnergyBandData,
1011
SteadyFreeCarrierData,
1112
SteadyPotentialData,
@@ -16,6 +17,7 @@
1617
TemperatureData,
1718
SteadyPotentialData,
1819
SteadyFreeCarrierData,
20+
SteadyElectricFieldData,
1921
SteadyEnergyBandData,
2022
SteadyCapacitanceData,
2123
]

tidy3d/components/tcad/monitors/charge.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,22 @@ class SteadyCapacitanceMonitor(HeatChargeMonitor):
8080
title="Unstructured Grid",
8181
description="Return data on the original unstructured grid.",
8282
)
83+
84+
85+
class SteadyElectricFieldMonitor(HeatChargeMonitor):
86+
"""
87+
Electric field monitor for Charge simulations.
88+
89+
Example
90+
-------
91+
>>> import tidy3d as td
92+
>>> electric_field_monitor_z0 = td.SteadyElectricFieldMonitor(
93+
... center=(0, 0.14, 0), size=(0.6, 0.3, 0), name="electric_field_z0",
94+
... )
95+
"""
96+
97+
unstructured: Literal[True] = pd.Field(
98+
True,
99+
title="Unstructured Grid",
100+
description="Return data on the original unstructured grid.",
101+
)

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+
SteadyElectricFieldMonitor,
1617
SteadyEnergyBandMonitor,
1718
SteadyFreeCarrierMonitor,
1819
SteadyPotentialMonitor,
@@ -34,6 +35,7 @@
3435
SteadyPotentialMonitor,
3536
SteadyFreeCarrierMonitor,
3637
SteadyEnergyBandMonitor,
38+
SteadyElectricFieldMonitor,
3739
SteadyCapacitanceMonitor,
3840
]
3941
HeatChargeSourceType = Union[HeatSource, HeatFromElectricSource, UniformHeatSource]

0 commit comments

Comments
 (0)