Source code for tidy3d.components.microwave.data.monitor_data

"""Post-processing data and figures of merit for antennas, including radiation efficiency,
reflection efficiency, gain, and realized gain.
"""

from __future__ import annotations

from typing import Optional

import pydantic.v1 as pd
import xarray as xr

from tidy3d.components.data.data_array import FieldProjectionAngleDataArray, FreqDataArray
from tidy3d.components.data.monitor_data import DirectivityData
from tidy3d.components.types import PolarizationBasis
from tidy3d.log import log


[docs] class AntennaMetricsData(DirectivityData): """Data representing the main parameters and figures of merit for antennas. Example ------- >>> import numpy as np >>> from tidy3d.components.data.monitor_data import FluxDataArray, FieldProjectionAngleDataArray >>> from tidy3d.components.monitor import DirectivityMonitor >>> f = np.linspace(1e14, 2e14, 10) >>> r = np.atleast_1d(1e6) >>> theta = np.linspace(0, np.pi, 10) >>> phi = np.linspace(0, 2*np.pi, 20) >>> coords = dict(r=r, theta=theta, phi=phi, f=f) >>> coords_flux = dict(f=f) >>> field_values = (1+1j) * np.random.random((len(r), len(theta), len(phi), len(f))) >>> flux_data = FluxDataArray(np.random.random(len(f)), coords=coords_flux) >>> scalar_field = FieldProjectionAngleDataArray(field_values, coords=coords) >>> monitor = DirectivityMonitor( ... center=(1,2,3), ... size=(2,2,2), ... freqs=f, ... name="rad_monitor", ... phi=phi, ... theta=theta ... ) # doctest: +SKIP >>> power_data = FreqDataArray(np.random.random(len(f)), coords=coords_flux) # doctest: +SKIP >>> data = AntennaMetricsData( ... monitor=monitor, ... projection_surfaces=monitor.projection_surfaces, ... flux=flux_data, ... Er=scalar_field, ... Etheta=scalar_field, ... Ephi=scalar_field, ... Hr=scalar_field, ... Htheta=scalar_field, ... Hphi=scalar_field, ... power_incident=power_data, ... power_reflected=power_data ... ) # doctest: +SKIP Notes ----- The definitions of radiation efficiency, reflection efficiency, gain, and realized gain are based on: Balanis, Constantine A., "Antenna Theory: Analysis and Design," John Wiley & Sons, Chapter 2.9 (2016). """ power_incident: FreqDataArray = pd.Field( ..., title="Power incident", description="Array of values representing the incident power to an antenna.", ) power_reflected: FreqDataArray = pd.Field( ..., title="Power reflected", description="Array of values representing power reflected due to an impedance mismatch with the antenna.", )
[docs] @staticmethod def from_directivity_data( dir_data: DirectivityData, power_inc: FreqDataArray, power_refl: FreqDataArray ) -> AntennaMetricsData: """Create :class:`.AntennaMetricsData` from directivity data and power measurements. Parameters ---------- dir_data : :class:`.DirectivityData` Directivity data containing field components and flux measurements. power_inc : :class:`.FreqDataArray` Array of values representing the incident power to an antenna. power_refl : :class:`.FreqDataArray` Array of values representing power reflected due to impedance mismatch with the antenna. Returns ------- :class:`.AntennaMetricsData` New instance combining directivity data with incident and reflected power measurements. """ antenna_params_dict = { **dir_data.dict(), "power_incident": power_inc, "power_reflected": power_refl, } antenna_params_dict.pop("type") return AntennaMetricsData(**antenna_params_dict)
@property def supplied_power(self) -> FreqDataArray: """The power supplied to the antenna, which takes into account reflections.""" return self.power_incident - self.power_reflected @property def radiation_efficiency(self) -> FreqDataArray: """The radiation efficiency of the antenna.""" return self.calc_radiation_efficiency(self.supplied_power) @property def reflection_efficiency(self) -> FreqDataArray: """The reflection efficiency of the antenna, which is due to an impedance mismatch.""" reflection_efficiency = self.supplied_power / self.power_incident return reflection_efficiency
[docs] def partial_gain( self, pol_basis: PolarizationBasis = "linear", tilt_angle: Optional[float] = None ) -> xr.Dataset: """The partial gain figures of merit for antennas. The partial gains are computed in the ``linear`` or ``circular`` polarization bases. If ``tilt_angle`` is not ``None``, the partial directivity is computed in the linear polarization basis rotated by ``tilt_angle`` from the theta-axis. Gain is dimensionless. Parameters ---------- pol_basis : PolarizationBasis The desired polarization basis used to express partial gain, either ``linear`` or ``circular``. tilt_angle : float The angle by which the co-polar vector is rotated from the theta-axis. At ``tilt_angle`` = 0, the co-polar vector coincides with the theta-axis and the cross-polar vector coincides with the phi-axis; while at ``tilt_angle = pi/2``, the co-polar vector coincides with the phi-axis. Returns ------- ``xarray.Dataset`` Dataset containing the partial gains split into the two polarization states. """ self._check_valid_pol_basis(pol_basis, tilt_angle) partial_D = self.partial_directivity(pol_basis=pol_basis, tilt_angle=tilt_angle) if pol_basis == "linear": if tilt_angle is None: rename_mapping = {"Dtheta": "Gtheta", "Dphi": "Gphi"} else: rename_mapping = {"Dco": "Gco", "Dcross": "Gcross"} else: rename_mapping = {"Dright": "Gright", "Dleft": "Gleft"} return self.radiation_efficiency * partial_D.rename(rename_mapping)
@property def gain(self) -> FieldProjectionAngleDataArray: """The gain figure of merit for antennas. Gain is dimensionless.""" partial_G = self.partial_gain() return partial_G.Gtheta + partial_G.Gphi
[docs] def partial_realized_gain( self, pol_basis: PolarizationBasis = "linear", tilt_angle: Optional[float] = None ) -> xr.Dataset: """The partial realized gain figures of merit for antennas. The partial gains are computed in the ``linear`` or ``circular`` polarization bases. If ``tilt_angle`` is not ``None``, the partial directivity is computed in the linear polarization basis rotated by ``tilt_angle`` from the theta-axis. Gain is dimensionless. Parameters ---------- pol_basis : PolarizationBasis The desired polarization basis used to express partial gain, either ``linear`` or ``circular``. tilt_angle : float The angle by which the co-polar vector is rotated from the theta-axis. At ``tilt_angle`` = 0, the co-polar vector coincides with the theta-axis and the cross-polar vector coincides with the phi-axis; while at ``tilt_angle = pi/2``, the co-polar vector coincides with the phi-axis. Returns ------- ``xarray.Dataset`` Dataset containing the partial realized gains split into the two polarization states. """ self._check_valid_pol_basis(pol_basis, tilt_angle) reflection_efficiency = self.reflection_efficiency partial_G = self.partial_gain(pol_basis=pol_basis, tilt_angle=tilt_angle) return reflection_efficiency * partial_G
@property def realized_gain(self) -> FieldProjectionAngleDataArray: """The realized gain figure of merit for antennas. Realized gain is dimensionless.""" partial_G = self.partial_realized_gain() return partial_G.Gtheta + partial_G.Gphi @pd.root_validator(pre=False) def _warn_rf_license(cls, values): log.warning( "โ„น๏ธ โš ๏ธ RF simulations are subject to new license requirements in the future. You have instantiated at least one RF-specific component.", log_once=True, ) return values