
.. currentmodule:: tidy3d

Monitors
========

Overview
--------

The full space-time distribution of the EM field is typically too large to efficiently store on disk or send over networks. Instead, we use monitors to record specific subsets of the field distribution, or derived quantities, that are relevant to our simulation goals.

The types of monitors in Tidy3D include:

* `Field`_: Records the EM field components in a spatial region (from 0D up to 3D) at specified time or frequency points
* `Flux`_: Records EM power flow across a 2D surface or 3D bounding box at specified time or frequency points
* `Mode`_: Records mode coefficient(s) of the field across a 2D plane, at specified frequency points or as a time series
* `Diffraction`_: Records diffraction coefficient(s) in a periodic simulation
* `Far-field`_: Various monitors for calculating far-field projection and radiation characteristics
* `Surface`_: Records EM fields on PEC and lossy metal surfaces in a 3D region at specified time or frequency points
* `Permittivity and Permeability`_: Records material permittivity or permeability properties within a given region

.. seealso::

   To learn more about accessing and plotting monitor data, please refer to the following learning center articles:

   + `Performing visualization of simulation data <../notebooks/VizData.html>`_
   + `Advanced monitor data (xarray) manipulation and visualization <../notebooks/XarrayTutorial.html>`_
   + `Creating FDTD animations <../notebooks/AnimationTutorial.html>`_

~~~~

Field 
-----

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   FieldMonitor
   PointCloudFieldMonitor
   DipoleEmissionMonitor
   FieldTimeMonitor

The ``FieldMonitor`` records the EM field components within a spatial region at
specified frequency point(s). The ``PointCloudFieldMonitor`` records
frequency-domain EM field components at arbitrary point coordinates. The
``DipoleEmissionMonitor`` records reduced dipole-emission radiation intensity at
point-cloud positions and is typically created by the
:doc:`dipole emission plugin <plugins/dipole_emission>`. The
``FieldTimeMonitor`` does the same as ``FieldMonitor``, except at specified time
intervals instead of frequency.

Point-cloud field data is indexed by point rather than by structured ``x`` / ``y`` / ``z`` grid coordinates, so structured-grid plotting helpers such as ``SimulationData.plot_field()`` do not support ``PointCloudFieldData``.
For point-cloud monitors, ``Dx``, ``Dy``, and ``Dz`` store electric displacement divided by ``epsilon_0`` (``D / epsilon_0``), where ``epsilon_0`` is the vacuum permittivity. They are reconstructed from raw E-field samples and the matching directional relative permittivity on the Yee grid: ``Dx`` uses ``Ex`` with x-direction permittivity, ``Dy`` uses ``Ey`` with y-direction permittivity, and ``Dz`` uses ``Ez`` with z-direction permittivity. The reconstructed values have the same units as the electric field.

.. code-block:: python

   # define a 2D field monitor at frequency f0
   my_field_monitor = FieldMonitor(
       center=(0,0,0),
       size=(10,10,0),
       name='My field monitor',
       freqs=[f0],
   )

   # define a point-cloud field monitor at arbitrary coordinates
   my_point_cloud_monitor = PointCloudFieldMonitor(
       points=PointDataArray(
           [[0.0, 0.0, 0.0], [0.1, 0.2, 0.3]],
           coords={"index": [0, 1], "axis": [0, 1, 2]},
       ),
       fields=["Ex", "Hy"],
       name='My point-cloud monitor',
       freqs=[f0],
   )

   # define a 1D field-time monitor starting at 1ps
   my_fieldtime_monitor = FieldTimeMonitor(
       center=(0,0,0),
       size=(10,0,0),
       name='My fieldtime monitor',
       start=1e-12,
       interval=50,   # number of solver time steps between each measurement
   )

.. note::

   The amount of data generated can be very large when recording 2D or 3D field information across a large number of time or frequency points. To save space, consider using the ``interval_space`` parameter to downsample the grid resolution, or the ``interval``, ``freqs`` parameters to reduce the number of time/frequency points.

~~~~

Flux
----

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   FluxMonitor
   FluxTimeMonitor

The ``FluxMonitor`` records EM power flux through a 2D surface or 3D bounding box at specified frequency point(s). The ``FluxTimeMonitor`` does the same, except at specified time intervals instead of frequency.

.. code-block:: python

   # define 2D flux monitor at frequency f0
   # to record power flux in the +z direction
   my_flux_monitor = FluxMonitor(
       center=(0,0,0),
       size=(10,10,0),
       name='My flux monitor',
       normal_dir='+',
       freqs=[f0],
   )

   # define a 3D flux-time monitor to record outgoing power vs time
   my_fluxtime_monitor = FluxTimeMonitor(
       center=(0,0,0),
       size=(10,10,10),
       name='My flux-time monitor',
       interval=50,    # number of solver time steps between each measurement
   )

.. seealso::

   Example applications:

   + `Scattering of a plasmonic nanoparticle <../notebooks/PlasmonicNanoparticle.html>`_
   + `Plasmonic Yagi-Uda nanoantenna <../notebooks/PlasmonicYagiUdaNanoantenna.html>`_

~~~~

Mode
----

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   ModeSpec
   ModeMonitor
   ModeSolverMonitor
   ModeTimeMonitor

The ``ModeMonitor`` records the mode coefficients of the incident field at specified frequency point(s).

Behind the scenes, a mode solver simulation is first performed to determine the eigenmodes in the ``ModeMonitor`` plane. A ``ModeSpec`` instance is necessary to provide the settings for this calculation. Then, the resulting modes are used to calculate the mode coefficients. The user does not need to explicitly perform the mode solver simulation, as it is automatically performed whenever a ``ModeSource`` or ``ModeMonitor`` is present in the simulation.

.. code-block:: python

   # define a mode spec
   # search for 3 modes near effective index of 2.2
   my_mode_spec = ModeSpec(num_modes=3, target_neff=2.2)

   # define a mode monitor at 20 freq points between 240 and 300 THz
   my_mode_monitor = ModeMonitor(
       center=(10,0,0),
       size=(0,20,20),
       name='My mode monitor',
       freqs=np.linspace(240e12, 300e12, 20),
       mode_spec=my_mode_spec,
   )

The ``ModeTimeMonitor`` is the time-domain counterpart of the ``ModeMonitor``: instead of recording mode coefficients at discrete frequency points, it records a complex-valued modal amplitude time series for each mode and propagation direction at the monitor plane. The modes are solved at a single frequency (``freq_spec``, defaulting to the central frequency of the first source).

.. code-block:: python

   # define a mode-time monitor recording modal amplitudes from 1ps to 5ps
   my_mode_time_monitor = ModeTimeMonitor(
       center=(10,0,0),
       size=(0,20,20),
       name='My mode time monitor',
       start=1e-12,
       stop=5e-12,
       mode_spec=my_mode_spec,
   )

.. seealso::

   For more details and examples, please refer to the following learning center article:

   + `Defining mode sources and monitors <../notebooks/ModalSourcesMonitors.html>`_

   Example applications:

   + `Waveguide Y-junction <../notebooks/YJunction.html>`_
   + `Broadband directional coupler <../notebooks/BroadbandDirectionalCoupler.html>`_

~~~~

Diffraction
-----------

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   DiffractionMonitor

The ``DiffractionMonitor`` records the diffraction coefficients of the allowed diffraction orders in a periodic simulation.

.. code-block:: python

   # define a diffraction monitor at 20 freq points between 250 and 300 THz
   # for outgoing fields in the +z direction
   my_diffraction_monitor = DiffractionMonitor(
       center=(0,0,10),
       size=(td.inf, td.inf, 0),    # extend monitor to simulation edges
       freqs=np.linspace(250e12, 300e12, 20),
       name='My diffraction monitor',
       normal_dir='+',
   )

For more detailed examples, please refer to the demo models linked below. 

.. seealso::

   Example applications:

   + `Multilevel blazed diffraction grating <../notebooks/GratingEfficiency.html>`_
   + `Mid-IR metalens based on silicon nanopillars <../notebooks/MidIRMetalens.html>`_

~~~~

Gaussian Overlap
----------------

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   GaussianOverlapMonitor
   AstigmaticGaussianOverlapMonitor
   ThinLensOverlapMonitor

These monitors decompose fields onto Gaussian or thin-lens beams to record overlap amplitudes.

Use ``ThinLensOverlapMonitor`` with the same thin-lens parameters as a
``ThinLensBeam`` source to measure coupling into the corresponding focused beam. The
monitor uses the same vectorial thin-lens angular spectrum as the source and analytic
beam profile. The ``numerical_aperture`` may exceed one in high-index media as long as
it is less than the real background refractive index. Gaussian-like overlap monitor
planes must lie in a homogeneous, isotropic background medium.

.. code-block:: python

   thin_lens_overlap = ThinLensOverlapMonitor(
       center=(0, 0, 0),
       size=(0, td.inf, td.inf),
       freqs=[f0],
       name='thin_lens_overlap',
       numerical_aperture=0.45,
       waist_distance=0.0,
       fill_lens=True,
       num_plane_waves=51,
       pol_angle=0,
   )

The monitor plane should be placed on the desired beam cross section, and
``waist_distance`` should describe that plane's signed axial distance from focus in the
beam propagation frame. If the monitor is used to analyze fields propagated from a
``ThinLensBeam`` source, update ``waist_distance`` by the source-to-monitor separation
along the tilted beam axis. For angled beams, also place the monitor ``center`` on the
tilted optical axis at the monitor plane. If ``d`` is the signed source-to-monitor
separation along the monitor normal coordinate, shift the two transverse coordinates by
``d * tan(angle_theta) * (cos(angle_phi), sin(angle_phi))`` in the monitor transverse-axis
order.

``lens_offset`` shifts the ideal focal field in the two tangential directions through a
pupil phase ramp. The entries follow the tangential-axis order obtained by removing the
normal axis from ``(x, y, z)``; for example, a y-normal monitor uses ``(x, z)``. It does
not model a decentered finite aperture with clipping or changed pupil amplitude.

For angled monitors, ``angle_theta`` and ``angle_phi`` rotate the ideal focused beam and
its pupil. As with ``ThinLensBeam``, this describes a tilted optical axis with an ideal
pupil perpendicular to the axis, not aberrated focusing from an obliquely illuminated
fixed lens surface.

~~~~

Far-field
---------

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   FieldProjectionCartesianMonitor
   FieldProjectionAngleMonitor
   FieldProjectionKSpaceMonitor
   rf.DirectivityMonitor
   AuxFieldTimeMonitor

The far-field monitor records the near-field within the simulation domain in order to project it to some far away location. This can be a very efficient way to simulate the scattering or radiative response of devices such as lenses and antenna.

.. code-block:: python

   # define a far-field monitor
   my_far_field_monitor = FieldProjectionCartesianMonitor(
       center=(0,0,10),
       size=(td.inf, td.inf, 0),
       name="My far field",
       freqs=[f0],
       far_field_approx=True,
       proj_axis=2,
       proj_distance=20,
       x=np.linspace(-10,10,101),
       y=np.linspace(-10,10,101),
   )

The ``far_field_approx`` parameter should be set to ``True`` (default) when the projection plane is far from the simulation domain. For intermediate distances, the user can set it to ``False`` for increased accuracy at the expense of slightly greater computational cost. 

When including far-field projection monitors in the :class:`.Simulation` object, the far-field calculation is performed server-side. The computation is much faster and slightly more accurate than if performed locally. However, it will slightly increase the cost of the simulation.

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   FieldProjector
   FieldProjectionSurface

The user also has the option of performing the far-field calculation on their local machine using ``FieldProjector`` object. In that case, they should first set up one or more ``FieldMonitor`` in the simulation. After the simulation is completed, the ``FieldProjector`` object operates on the recorded field data.

.. code-block:: python

   # define a far-field projector from previously defined field monitors
   my_far_field_projector = FieldProjector.from_near_field_monitors(
       sim_data = simulation_data,    # previously computed simulation data
       near_monitors = [monitor1],    # list of previously defined near field monitors
       normal_dirs=['+'],             # projection direction relative to monitor
       pts_per_wavelength=10          # controls sampling rate (reduce to speed up computation)
   )

   # project field based on previously defined far-field monitor
   projected_field = my_far_field_projector.project_fields(my_far_field_monitor)

Please see the learning center article below for detailed explanations on additional settings and usage scenarios. 

.. seealso::

   For more details and examples, please refer to the following learning center article:

   + `Performing near field to far field projections <../notebooks/FieldProjections.html>`_

   Example applications:

   + `Mid-IR metalens based on silicon nanopillars <../notebooks/MidIRMetalens.html>`_
   + `Spherical Fresnel lens <../notebooks/FresnelLens.html>`_
   + `Directivity and S-parameters computation of patch antenna <../notebooks/AntennaCharacteristics.html>`_

~~~~

Surface
-------

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   SurfaceFieldMonitor
   SurfaceFieldTimeMonitor

The ``SurfaceFieldMonitor`` records electromagnetic fields on PEC (``PECMedium``) and lossy metal (``LossyMetalMedium``) with ``penetrable=False`` within a 3D region at specified frequency point(s). The ``SurfaceFieldTimeMonitor`` does the same, except at specified time intervals instead of frequency. These monitors store the normal E and tangential H fields on these surfaces.

.. code-block:: python

   # define a frequency-domain surface field monitor
   my_surface_monitor = SurfaceFieldMonitor(
       center=(1,2,3),
       size=(2,2,2),
       fields=['E', 'H'],
       freqs=[250e12, 300e12],
       name='surface_monitor',
   )

   # define a time-domain surface field monitor
   my_surface_time_monitor = SurfaceFieldTimeMonitor(
       center=(1,2,3),
       size=(2,2,2),
       fields=['H'],
       start=1e-13,
       stop=5e-13,
       interval=2,
       name='surface_time_monitor',
   )

.. note::

   Surface monitors are currently in beta stage. Please exercise caution when analyzing surface monitor data and verify results carefully.


~~~~

Permittivity and Permeability
-----------------------------

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   PermittivityMonitor
   PointCloudPermittivityMonitor
   MediumMonitor

The ``PermittivityMonitor`` is used to record local relative permittivity data in the region of interest, and the ``PointCloudPermittivityMonitor`` records local relative permittivity components for arbitrary requested point coordinates. ``PermittivityMonitor``, ``PointCloudPermittivityMonitor``, and ``MediumMonitor`` return material values on the simulation Yee grid. Near material interfaces, these may be effective grid values representing the discretized interface rather than the bulk value of exactly one adjacent material. The monitors do not apply additional interpolation or smoothing to user-requested coordinates. For ``PointCloudPermittivityMonitor``, each returned component is sampled from its nearest native Yee-grid location, using the ``eps_xx`` / ``eps_yy`` / ``eps_zz`` component grids associated with ``Ex`` / ``Ey`` / ``Ez`` respectively; values are not interpolated to the exact requested coordinates. The returned ``.points`` array stores the requested point cloud, not the snapped component-grid sampling locations. The ``MediumMonitor`` can additionally record permeability data. Those data can be useful for post-simulation calculations that require permittivity or permeability values, such as mode volume and absorption density.

Point-cloud permittivity data is indexed by point rather than by structured ``x`` / ``y`` / ``z`` grid coordinates. Access the components directly, for example ``sim_data["my point-cloud permittivity monitor"].eps_xx``, and inspect the requested coordinates with ``sim_data["my point-cloud permittivity monitor"].points``.

.. code-block:: python

   my_permittivity_monitor = PermittivityMonitor(
       center=(0,0,0),
       size=(8,8,8),
       freqs=[250e12, 300e12],
       name='my permittivity monitor',
   )

   my_point_cloud_permittivity_monitor = PointCloudPermittivityMonitor(
       points=PointDataArray(
           [[0.0, 0.0, 0.0], [0.1, 0.2, 0.3]],
           coords={"index": [0, 1], "axis": [0, 1, 2]},
       ),
       freqs=[250e12, 300e12],
       name='my point-cloud permittivity monitor',
   )

.. seealso::

   Example applications:

   + `Bistability in photonic crystal microcavities <../notebooks/BistablePCCavity.html>`_

~~~~

Apodization
-----------

.. autosummary::
   :toctree: _autosummary/
   :template: module.rst

   ApodizationSpec

The ``ApodizationSpec`` is used to specify apodization specifications for frequency-domain monitors. Typically, the default Tidy3D settings are acceptable and it is not necessary to define a custom instance. Please refer to the documentation page for more details.
