Skip to content

Commit 2116575

Browse files
surface monitors
1 parent 0b68e6d commit 2116575

File tree

11 files changed

+1111
-188
lines changed

11 files changed

+1111
-188
lines changed

tidy3d/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@
124124
FluxTimeDataArray,
125125
HeatDataArray,
126126
IndexedDataArray,
127+
IndexedFieldDataArray,
128+
IndexedFieldTimeDataArray,
129+
IndexedFreqDataArray,
130+
IndexedTimeDataArray,
127131
IndexedVoltageDataArray,
128132
ModeAmpsDataArray,
129133
ModeIndexDataArray,
@@ -158,6 +162,7 @@
158162
PermittivityData,
159163
)
160164
from .components.data.sim_data import DATA_TYPE_MAP, SimulationData
165+
from .components.data.unstructured.surface import TriangularSurfaceDataset
161166
from .components.data.utils import (
162167
TetrahedralGridDataset,
163168
TriangularGridDataset,
@@ -284,6 +289,8 @@
284289
ModeSolverMonitor,
285290
Monitor,
286291
PermittivityMonitor,
292+
SurfaceFieldMonitor,
293+
SurfaceFieldTimeMonitor,
287294
)
288295
from .components.parameter_perturbation import (
289296
CustomChargePerturbation,
@@ -625,9 +632,14 @@ def set_logging_level(level: str) -> None:
625632
"CellDataArray",
626633
"IndexedDataArray",
627634
"IndexedVoltageDataArray",
635+
"IndexedFieldDataArray",
636+
"IndexedFieldTimeDataArray",
637+
"IndexedFreqDataArray",
638+
"IndexedTimeDataArray",
628639
"SteadyVoltageDataArray",
629640
"TriangularGridDataset",
630641
"TetrahedralGridDataset",
642+
"TriangularSurfaceDataset",
631643
"medium_from_nk",
632644
"SubpixelSpec",
633645
"Staircasing",
@@ -676,4 +688,7 @@ def set_logging_level(level: str) -> None:
676688
"IsothermalSteadyChargeDCAnalysis",
677689
"ChargeToleranceSpec",
678690
"AntennaMetricsData",
691+
"SurfaceFieldMonitor",
692+
"SurfaceFieldTimeMonitor",
693+
"TriangularSurfaceDataset",
679694
]

tidy3d/components/data/data_array.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,66 @@ class SpatialVoltageDataArray(AbstractSpatialDataArray):
12531253
_dims = ("x", "y", "z", "voltage")
12541254

12551255

1256+
class IndexedFieldDataArray(DataArray):
1257+
"""Stores indexed values of vector fields in frequency domain. It is typically used
1258+
in conjuction with a ``PointDataArray`` to store point-associated vector data.
1259+
1260+
Example
1261+
-------
1262+
>>> indexed_array = IndexedFieldDataArray(
1263+
... (1+1j) * np.random.random((4,3,1)), coords=dict(index=np.arange(4), axis=np.arange(3), f=[1e9])
1264+
... )
1265+
"""
1266+
1267+
__slots__ = ()
1268+
_dims = ("index", "axis", "f")
1269+
1270+
1271+
class IndexedFieldTimeDataArray(DataArray):
1272+
"""Stores indexed values of vector fields in time domain. It is typically used
1273+
in conjuction with a ``PointDataArray`` to store point-associated vector data.
1274+
1275+
Example
1276+
-------
1277+
>>> indexed_array = IndexedFieldDataArray(
1278+
... (1+1j) * np.random.random((4,3,1)), coords=dict(index=np.arange(4), axis=np.arange(3), t=[0])
1279+
... )
1280+
"""
1281+
1282+
__slots__ = ()
1283+
_dims = ("index", "axis", "t")
1284+
1285+
1286+
class IndexedFreqDataArray(DataArray):
1287+
"""Stores indexed values of scalar fields in frequency domain. It is typically used
1288+
in conjuction with a ``PointDataArray`` to store point-associated vector data.
1289+
1290+
Example
1291+
-------
1292+
>>> indexed_array = IndexedFieldDataArray(
1293+
... (1+1j) * np.random.random((4,1)), coords=dict(index=np.arange(4), f=[1e9])
1294+
... )
1295+
"""
1296+
1297+
__slots__ = ()
1298+
_dims = ("index", "f")
1299+
1300+
1301+
class IndexedTimeDataArray(DataArray):
1302+
"""Stores indexed values of scalar fields in time domain. It is typically used
1303+
in conjuction with a ``PointDataArray`` to store point-associated vector data.
1304+
1305+
Example
1306+
-------
1307+
>>> indexed_array = IndexedFieldDataArray(
1308+
... (1+1j) * np.random.random((4,1)), coords=dict(index=np.arange(4), t=[0])
1309+
... )
1310+
"""
1311+
1312+
__slots__ = ()
1313+
_dims = ("index", "t")
1314+
1315+
12561316
DATA_ARRAY_TYPES = [
12571317
SpatialDataArray,
12581318
ScalarFieldDataArray,
@@ -1286,7 +1346,19 @@ class SpatialVoltageDataArray(AbstractSpatialDataArray):
12861346
CellDataArray,
12871347
IndexedDataArray,
12881348
IndexedVoltageDataArray,
1349+
IndexedFieldDataArray,
1350+
IndexedFieldTimeDataArray,
1351+
IndexedFreqDataArray,
1352+
IndexedTimeDataArray,
12891353
]
12901354
DATA_ARRAY_MAP = {data_array.__name__: data_array for data_array in DATA_ARRAY_TYPES}
12911355

1292-
IndexedDataArrayTypes = Union[IndexedDataArray, IndexedVoltageDataArray]
1356+
IndexedDataArrayTypes = Union[
1357+
IndexedDataArray,
1358+
IndexedVoltageDataArray,
1359+
IndexedFieldDataArray,
1360+
IndexedFieldTimeDataArray,
1361+
IndexedFreqDataArray,
1362+
IndexedTimeDataArray,
1363+
PointDataArray,
1364+
]

tidy3d/components/data/dataset.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from abc import ABC, abstractmethod
6-
from typing import Any, Callable, Dict, Optional, Union
6+
from typing import Any, Callable, Dict, Optional, Tuple, Union
77

88
import numpy as np
99
import pydantic.v1 as pd
@@ -28,6 +28,7 @@
2828
TimeDataArray,
2929
TriangleMeshDataArray,
3030
)
31+
from .unstructured.surface import TriangularSurfaceDataset
3132

3233
DEFAULT_MAX_SAMPLES_PER_STEP = 10_000
3334
DEFAULT_MAX_CELLS_PER_STEP = 10_000
@@ -394,6 +395,76 @@ class AuxFieldTimeDataset(AuxFieldDataset):
394395
)
395396

396397

398+
class ElectromagneticSurfaceFieldDataset(AbstractFieldDataset, ABC):
399+
"""Stores a collection of E and H fields with x, y, z components."""
400+
401+
E: Tuple[Optional[TriangularSurfaceDataset], Optional[TriangularSurfaceDataset]] = pd.Field(
402+
(None, None),
403+
title="E",
404+
description="Spatial distribution of the electric field on the internal and external sides of the surface.",
405+
)
406+
407+
H: Tuple[Optional[TriangularSurfaceDataset], Optional[TriangularSurfaceDataset]] = pd.Field(
408+
(None, None),
409+
title="H",
410+
description="Spatial distribution of the magnetic field on the internal and external sides of the surface.",
411+
)
412+
413+
normal: TriangularSurfaceDataset = pd.Field(
414+
None,
415+
title="Surface Normal",
416+
description="Spatial distribution of the surface normal.",
417+
)
418+
419+
@property
420+
def field_components(self) -> Dict[str, DataArray]:
421+
"""Maps the field components to their associated data."""
422+
fields = {
423+
"E": self.E,
424+
"H": self.H,
425+
}
426+
return {field_name: field for field_name, field in fields.items() if field is not None}
427+
428+
@property
429+
def current_density(self) -> ElectromagneticSurfaceFieldDataset:
430+
"""Surface current density."""
431+
432+
h_diff = 0
433+
template = None
434+
# we assume that is data is None it means field is zero on that side (e.g. PEC)
435+
if self.H[0] is not None:
436+
h_diff += self.H[0].values
437+
template = self.H[0]
438+
if self.H[1] is not None:
439+
h_diff -= self.H[1].values
440+
template = self.H[1]
441+
442+
if template is None:
443+
raise ValueError(
444+
"Could not calculate current density: the dataset does not contain H field information."
445+
)
446+
447+
return template.updated_copy(values=xr.cross(h_diff, self.normal.values, dim="axis"))
448+
449+
@property
450+
def grid_locations(self) -> Dict[str, str]:
451+
"""Maps field components to the string key of their grid locations on the yee lattice."""
452+
raise RuntimeError("Function 'grid_location' does not apply to surface monitors.")
453+
454+
@property
455+
def symmetry_eigenvalues(self) -> Dict[str, Callable[[Axis], float]]:
456+
"""Maps field components to their (positive) symmetry eigenvalues."""
457+
458+
return dict(
459+
Ex=lambda dim: -1 if (dim == 0) else +1,
460+
Ey=lambda dim: -1 if (dim == 1) else +1,
461+
Ez=lambda dim: -1 if (dim == 2) else +1,
462+
Hx=lambda dim: +1 if (dim == 0) else -1,
463+
Hy=lambda dim: +1 if (dim == 1) else -1,
464+
Hz=lambda dim: +1 if (dim == 2) else -1,
465+
)
466+
467+
397468
class ModeSolverDataset(ElectromagneticFieldDataset):
398469
"""Dataset storing scalar components of E and H fields as a function of freq. and mode_index.
399470

0 commit comments

Comments
 (0)