"""Monitor level data, store the DataArrays associated with a single heat-charge monitor."""
from __future__ import annotations
from typing import Optional, Union
import pydantic.v1 as pd
from tidy3d.components.base import skip_if_fields_missing
from tidy3d.components.data.data_array import (
DataArray,
ScalarFieldTimeDataArray,
SpatialDataArray,
)
from tidy3d.components.data.utils import TetrahedralGridDataset, TriangularGridDataset
from tidy3d.components.tcad.data.monitor_data.abstract import HeatChargeMonitorData
from tidy3d.components.tcad.monitors.heat import (
TemperatureMonitor,
)
from tidy3d.components.types import annotate_type
from tidy3d.constants import KELVIN
from tidy3d.log import log
FieldDataset = Union[
SpatialDataArray,
ScalarFieldTimeDataArray,
annotate_type(Union[TriangularGridDataset, TetrahedralGridDataset]),
]
UnstructuredFieldType = Union[TriangularGridDataset, TetrahedralGridDataset]
[docs]
class TemperatureData(HeatChargeMonitorData):
"""Data associated with a :class:`TemperatureMonitor`: spatial temperature field.
Example
-------
>>> from tidy3d import TemperatureMonitor, SpatialDataArray
>>> import numpy as np
>>> temp_data = SpatialDataArray(
... np.ones((2, 3, 4)), coords={"x": [0, 1], "y": [0, 1, 2], "z": [0, 1, 2, 3]}
... )
>>> temp_mnt = TemperatureMonitor(size=(1, 2, 3), name="temperature")
>>> temp_mnt_data = TemperatureData(
... monitor=temp_mnt, temperature=temp_data, symmetry=(0, 1, 0), symmetry_center=(0, 0, 0)
... )
>>> temp_mnt_data_expanded = temp_mnt_data.symmetry_expanded_copy
"""
monitor: TemperatureMonitor = pd.Field(
..., title="Monitor", description="Temperature monitor associated with the data."
)
temperature: Optional[FieldDataset] = pd.Field(
...,
title="Temperature",
description="Spatial temperature field.",
units=KELVIN,
)
@property
def field_components(self) -> dict[str, DataArray]:
"""Maps the field components to their associated data."""
return {"temperature": self.temperature}
[docs]
@pd.validator("temperature", always=True)
@skip_if_fields_missing(["monitor"])
def warn_no_data(cls, val, values):
"""Warn if no data provided."""
mnt = values.get("monitor")
if val is None:
log.warning(
f"No data is available for monitor '{mnt.name}'. This is typically caused by "
"monitor not intersecting any solid medium."
)
return val
[docs]
def field_name(self, val: str = "") -> str:
"""Gets the name of the fields to be plot."""
if val == "abs^2":
return "|T|ยฒ, Kยฒ"
else:
return "T, K"
@property
def symmetry_expanded_copy(self) -> TemperatureData:
"""Return copy of self with symmetry applied."""
new_temp = self._symmetry_expanded_copy(property=self.temperature)
return self.updated_copy(temperature=new_temp, symmetry=(0, 0, 0))