diff --git a/CHANGELOG.md b/CHANGELOG.md index 487e6b9581..4aafc0e947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +### Changed + +### Fixed + + +## [v2.9.1] - 2025-08-13 + +### Changed +- Validate mode solver object for large number of grid points on the modal plane. + +### Fixed +- Fixed missing amplitude factor and handling of negative normal direction case when making adjoint sources from `DiffractionMonitor`. +- Improved the robustness of batch jobs. The batch state, including all `task_ids`, is now saved to `batch.hdf5` immediately after upload. This fixes an issue where an interrupted batch (e.g., due to a kernel crash or network loss) would be unrecoverable. +- Fixed warning for running symmetric adjoint simulations by port to not trigger when there is a single port. +- Bug in `CoaxialLumpedPort` where source injection is off when the `normal_axis` is not `z`. +- Validation of `freqs` in the `ComponentModeler` and `TerminalComponentModeler`. +- Calculation of voltage and current in the `WavePort`, when one type of path integral is supplied and the transmission line mode is lossy. +- Polygon vertices cleanup in `ClipOperation.intersections_plane`. +- Removed sources from `sim_inf_structure` simulation object in `postprocess_adj` to avoid source and background medium validation errors. +- Revert overly restrictive validation of `freqs` in the `ComponentModeler` and `TerminalComponentModeler`. +- Fixed `ElectromagneticFieldData.to_zbf()` to support single frequency monitors and apply the correct flattening order. + ## [2.9.0] - 2025-08-04 ### Added diff --git a/docs/api/index.rst b/docs/api/index.rst index 591c2ebfdb..ce873a2b13 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -23,6 +23,7 @@ API |:computer:| output_data scene logging + utilities submit_simulations mesh/index heat/index @@ -37,6 +38,7 @@ API |:computer:| viz .. include:: /api/simulation.rst +.. include:: /api/utilities.rst .. include:: /api/boundary_conditions.rst .. include:: /api/geometry.rst .. include:: /api/mediums.rst @@ -62,4 +64,4 @@ API |:computer:| .. include:: /api/constants.rst .. include:: /api/abstract_base.rst .. include:: /api/abstract_models.rst -.. include:: /api/viz.rst \ No newline at end of file +.. include:: /api/viz.rst diff --git a/docs/api/simulation.rst b/docs/api/simulation.rst index 79c6a414bb..7d5ee25dbf 100644 --- a/docs/api/simulation.rst +++ b/docs/api/simulation.rst @@ -8,4 +8,3 @@ Simulation :template: module.rst tidy3d.Simulation - tidy3d.RunTimeSpec diff --git a/docs/api/utilities.rst b/docs/api/utilities.rst new file mode 100644 index 0000000000..0573e223a3 --- /dev/null +++ b/docs/api/utilities.rst @@ -0,0 +1,14 @@ +.. currentmodule:: tidy3d + +Utilities +========= + +.. autosummary:: + :toctree: _autosummary/ + :template: module.rst + + tidy3d.RunTimeSpec + tidy3d.FreqRange + tidy3d.FrequencyUtils + tidy3d.frequencies + tidy3d.wavelengths diff --git a/pyproject.toml b/pyproject.toml index b4be4d48de..42d8e81d9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "tidy3d" -version = "2.9.0" +version = "2.9.1" description = "A fast FDTD solver" authors = ["Tyler Hughes "] license = "LGPLv2+" diff --git a/schemas/EMESimulation.json b/schemas/EMESimulation.json index c452635fd8..00242d01bd 100644 --- a/schemas/EMESimulation.json +++ b/schemas/EMESimulation.json @@ -1,6 +1,6 @@ { "title": "EMESimulation", - "description": "EigenMode Expansion (EME) simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[NoneType, ...] = ()\n Sources in the simulation. NOTE: sources are not currently supported for EME simulations. Instead, the simulation performs full bidirectional propagation in the 'port_mode' basis. After running the simulation, use 'smatrix_in_basis' to use another set of modes or input field.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), y=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), z=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. By default, PEC boundary conditions are applied on all sides. This field is for consistency with FDTD simulations; however, please note that regardless of the 'boundary_spec', the mode solver terminates the mode plane with PEC boundary. The 'EMEModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[Annotated[Union[tidy3d.components.eme.monitor.EMEModeSolverMonitor, tidy3d.components.eme.monitor.EMEFieldMonitor, tidy3d.components.eme.monitor.EMECoefficientMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.PermittivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions. This is distinct from 'eme_grid_spec', which defines the 1D EME grid in the propagation direction.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n Frequencies for the EME simulation. The field is propagated independently at each provided frequency. This can be slow when the number of frequencies is large. In this case, consider using the approximate 'EMEFreqSweep' as the 'sweep_spec' instead of providing all desired frequencies here.\naxis : Literal[0, 1, 2]\n Propagation axis (0, 1, or 2) for the EME simulation.\neme_grid_spec : Union[EMEUniformGrid, EMECompositeGrid, EMEExplicitGrid]\n Specification for the EME propagation grid. The simulation is divided into cells in the propagation direction; this parameter specifies the layout of those cells. Mode solving is performed in each cell, and then propagation between cells is performed to determine the complete solution. This is distinct from 'grid_spec', which defines the grid in the two tangential directions, as well as the grid used for field monitors.\nstore_port_modes : bool = True\n Whether to store the modes associated with the two ports. Required to find scattering matrix in basis besides the computational basis.\nnormalize : bool = True\n Whether to normalize the port modes to unity flux, thereby normalizing the scattering matrix and expansion coefficients.\nport_offsets : Tuple[NonNegativeFloat, NonNegativeFloat] = (0, 0)\n Offsets for the two ports, relative to the simulation bounds along the propagation axis.\nsweep_spec : Union[EMELengthSweep, EMEModeSweep, EMEFreqSweep, EMEPeriodicitySweep, NoneType] = None\n Specification for a parameter sweep to be performed during the EME propagation step. The results are stored in 'sim_data.smatrix'. Other simulation monitor data is not included in the sweep.\nconstraint : Optional[Literal['passive', 'unitary']] = passive\n Constraint for EME propagation, imposed at cell interfaces. A constraint of 'passive' means that energy can be dissipated but not created at interfaces. A constraint of 'unitary' means that energy is conserved at interfaces (but not necessarily within cells). The option 'none' may be faster for a large number of modes. The option 'passive' can serve as regularization for the field continuity requirement and give more physical results.\n\nNotes\n-----\n\n EME is a frequency-domain method for propagating the electromagnetic field along a\n specified axis. The method is well-suited for propagation of guided waves.\n The electromagnetic fields are expanded locally in the basis of eigenmodes of the\n waveguide; they are then propagated by imposing continuity conditions in this basis.\n\n The EME simulation is performed along the propagation axis ``axis`` at frequencies ``freqs``.\n The simulation is divided into cells along the propagation axis, as defined by\n ``eme_grid_spec``. Mode solving is performed at cell centers, and boundary conditions are\n imposed between cells. The EME ports are defined to be the boundaries of the first and last\n cell in the EME grid. These can be moved using ``port_offsets``.\n\n An EME simulation always computes the full scattering matrix of the structure.\n Additional data can be recorded by adding 'monitors' to the simulation.\n\n **Other Bases**\n\n By default, the scattering matrix is expressed in the basis of EME modes at the two ports. It is sometimes useful to use alternative bases. For example, in a waveguide splitter, we might want the scattering matrix in the basis of modes of the individual waveguides. The functions `smatrix_in_basis` and `field_in_basis` in :class:`.EMESimulationData` can be used for this purpose after the simulation has been run.\n\n **Frequency Sweeps**\n\n Frequency sweeps are supported by including multiple frequencies in the `freqs` field. However, our EME solver repeats the mode solving for each new frequency, so frequency sweeps involving a large number of frequencies can be slow and expensive. If a large number of frequencies are required, consider using our FDTD solver instead.\n\n **Passivity and Unitarity Constraints**\n\n Passivity and unitarity constraints can be imposed via the `constraint` field. These constraints are imposed at interfaces between cells, possibly at the expense of field continuity. Passivity means that the interface can only dissipate energy, and unitarity means the interface will conserve energy (energy may still be dissipated inside cells when the propagation constant is complex). Adding constraints can slow down the simulation significantly, especially for a large number of modes (more than 30 or 40).\n\n **Too Many Modes**\n\n It is important to use enough modes to capture the physics of the device and to ensure that the results have converged (see below). However, using too many modes can slow down the simulation and result in numerical issues. If too many modes are used, it is common to see a warning about invalid modes in the simulation log. While these modes are not included in the EME propagation, this can indicate some other issue with the setup, especially if the results have not converged. In this case, extending the simulation size in the transverse directions and increasing the grid resolution may help by creating more valid modes that can be used in convergence testing.\n\n **Mode Convergence Sweeps**\n\n It is a good idea to check that the number of modes is large enough by running a mode convergence sweep. This can be done using :class:`.EMEModeSweep`.\n\nExample\n-------\n>>> from tidy3d import Box, Medium, Structure, C_0, inf\n>>> from tidy3d import EMEModeSpec, EMEUniformGrid, GridSpec\n>>> from tidy3d import EMEFieldMonitor\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> sim_size = 3*lambda0, 3*lambda0, 3*lambda0\n>>> waveguide_size = (lambda0/2, lambda0, inf)\n>>> waveguide = Structure(\n... geometry=Box(center=(0,0,0), size=waveguide_size),\n... medium=Medium(permittivity=2)\n... )\n>>> eme_grid_spec = EMEUniformGrid(num_cells=5, mode_spec=EMEModeSpec(num_modes=10))\n>>> grid_spec = GridSpec(wavelength=lambda0)\n>>> field_monitor = EMEFieldMonitor(\n... size=(0, sim_size[1], sim_size[2]),\n... name=\"field_monitor\"\n... )\n>>> sim = EMESimulation(\n... size=sim_size,\n... monitors=[field_monitor],\n... structures=[waveguide],\n... axis=2,\n... freqs=[freq0],\n... eme_grid_spec=eme_grid_spec,\n... grid_spec=grid_spec\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `EME Solver Demonstration <../../notebooks/docs/features/eme.rst>`_", + "description": "EigenMode Expansion (EME) simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[NoneType, ...] = ()\n Sources in the simulation. NOTE: sources are not currently supported for EME simulations. Instead, the simulation performs full bidirectional propagation in the 'port_mode' basis. After running the simulation, use 'smatrix_in_basis' to use another set of modes or input field.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), y=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), z=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. By default, PEC boundary conditions are applied on all sides. This field is for consistency with FDTD simulations; however, please note that regardless of the 'boundary_spec', the mode solver terminates the mode plane with PEC boundary. The 'EMEModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[Annotated[Union[tidy3d.components.eme.monitor.EMEModeSolverMonitor, tidy3d.components.eme.monitor.EMEFieldMonitor, tidy3d.components.eme.monitor.EMECoefficientMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.PermittivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions. This is distinct from 'eme_grid_spec', which defines the 1D EME grid in the propagation direction.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n Frequencies for the EME simulation. The field is propagated independently at each provided frequency. This can be slow when the number of frequencies is large. In this case, consider using the approximate 'EMEFreqSweep' as the 'sweep_spec' instead of providing all desired frequencies here.\naxis : Literal[0, 1, 2]\n Propagation axis (0, 1, or 2) for the EME simulation.\neme_grid_spec : Union[EMEUniformGrid, EMECompositeGrid, EMEExplicitGrid]\n Specification for the EME propagation grid. The simulation is divided into cells in the propagation direction; this parameter specifies the layout of those cells. Mode solving is performed in each cell, and then propagation between cells is performed to determine the complete solution. This is distinct from 'grid_spec', which defines the grid in the two tangential directions, as well as the grid used for field monitors.\nstore_port_modes : bool = True\n Whether to store the modes associated with the two ports. Required to find scattering matrix in basis besides the computational basis.\nnormalize : bool = True\n Whether to normalize the port modes to unity flux, thereby normalizing the scattering matrix and expansion coefficients.\nport_offsets : Tuple[NonNegativeFloat, NonNegativeFloat] = (0, 0)\n Offsets for the two ports, relative to the simulation bounds along the propagation axis.\nsweep_spec : Union[EMELengthSweep, EMEModeSweep, EMEFreqSweep, EMEPeriodicitySweep, NoneType] = None\n Specification for a parameter sweep to be performed during the EME propagation step. The results are stored in 'sim_data.smatrix'. Other simulation monitor data is not included in the sweep.\nconstraint : Optional[Literal['passive', 'unitary']] = passive\n Constraint for EME propagation, imposed at cell interfaces. A constraint of 'passive' means that energy can be dissipated but not created at interfaces. A constraint of 'unitary' means that energy is conserved at interfaces (but not necessarily within cells). The option 'none' may be faster for a large number of modes. The option 'passive' can serve as regularization for the field continuity requirement and give more physical results.\n\nNotes\n-----\n\n EME is a frequency-domain method for propagating the electromagnetic field along a\n specified axis. The method is well-suited for propagation of guided waves.\n The electromagnetic fields are expanded locally in the basis of eigenmodes of the\n waveguide; they are then propagated by imposing continuity conditions in this basis.\n\n The EME simulation is performed along the propagation axis ``axis`` at frequencies ``freqs``.\n The simulation is divided into cells along the propagation axis, as defined by\n ``eme_grid_spec``. Mode solving is performed at cell centers, and boundary conditions are\n imposed between cells. The EME ports are defined to be the boundaries of the first and last\n cell in the EME grid. These can be moved using ``port_offsets``.\n\n An EME simulation always computes the full scattering matrix of the structure.\n Additional data can be recorded by adding 'monitors' to the simulation.\n\n **Other Bases**\n\n By default, the scattering matrix is expressed in the basis of EME modes at the two ports. It is sometimes useful to use alternative bases. For example, in a waveguide splitter, we might want the scattering matrix in the basis of modes of the individual waveguides. The functions `smatrix_in_basis` and `field_in_basis` in :class:`.EMESimulationData` can be used for this purpose after the simulation has been run.\n\n **Frequency Sweeps**\n\n Frequency sweeps are supported by including multiple frequencies in the `freqs` field. However, our EME solver repeats the mode solving for each new frequency, so frequency sweeps involving a large number of frequencies can be slow and expensive. If a large number of frequencies are required, consider using our FDTD solver instead.\n\n **Passivity and Unitarity Constraints**\n\n Passivity and unitarity constraints can be imposed via the `constraint` field. These constraints are imposed at interfaces between cells, possibly at the expense of field continuity. Passivity means that the interface can only dissipate energy, and unitarity means the interface will conserve energy (energy may still be dissipated inside cells when the propagation constant is complex). Adding constraints can slow down the simulation significantly, especially for a large number of modes (more than 30 or 40).\n\n **Too Many Modes**\n\n It is important to use enough modes to capture the physics of the device and to ensure that the results have converged (see below). However, using too many modes can slow down the simulation and result in numerical issues. If too many modes are used, it is common to see a warning about invalid modes in the simulation log. While these modes are not included in the EME propagation, this can indicate some other issue with the setup, especially if the results have not converged. In this case, extending the simulation size in the transverse directions and increasing the grid resolution may help by creating more valid modes that can be used in convergence testing.\n\n **Mode Convergence Sweeps**\n\n It is a good idea to check that the number of modes is large enough by running a mode convergence sweep. This can be done using :class:`.EMEModeSweep`.\n\nExample\n-------\n>>> from tidy3d import Box, Medium, Structure, C_0, inf\n>>> from tidy3d import EMEModeSpec, EMEUniformGrid, GridSpec\n>>> from tidy3d import EMEFieldMonitor\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> sim_size = 3*lambda0, 3*lambda0, 3*lambda0\n>>> waveguide_size = (lambda0/2, lambda0, inf)\n>>> waveguide = Structure(\n... geometry=Box(center=(0,0,0), size=waveguide_size),\n... medium=Medium(permittivity=2)\n... )\n>>> eme_grid_spec = EMEUniformGrid(num_cells=5, mode_spec=EMEModeSpec(num_modes=10))\n>>> grid_spec = GridSpec(wavelength=lambda0)\n>>> field_monitor = EMEFieldMonitor(\n... size=(0, sim_size[1], sim_size[2]),\n... name=\"field_monitor\"\n... )\n>>> sim = EMESimulation(\n... size=sim_size,\n... monitors=[field_monitor],\n... structures=[waveguide],\n... axis=2,\n... freqs=[freq0],\n... eme_grid_spec=eme_grid_spec,\n... grid_spec=grid_spec\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `EME Solver Demonstration <../../notebooks/docs/features/eme.rst>`_", "type": "object", "properties": { "attrs": { @@ -434,7 +434,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { diff --git a/schemas/HeatChargeSimulation.json b/schemas/HeatChargeSimulation.json index eff2eb52cd..fe55b4efc5 100644 --- a/schemas/HeatChargeSimulation.json +++ b/schemas/HeatChargeSimulation.json @@ -1,6 +1,6 @@ { "title": "HeatChargeSimulation", - "description": "Defines thermoelectric simulations.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nNotes\n-----\n A ``HeatChargeSimulation`` supports different types of simulations. It solves the\n heat and conduction equations using the Finite-Volume (FV) method. This solver\n determines the required computation physics according to the simulation scene definition.\n This is implemented in this way due to the strong multi-physics coupling.\n\nThe ``HeatChargeSimulation`` can solve multiple physics and the intention is to enable close thermo-electrical coupling.\n\nCurrently, this solver supports steady-state heat conduction where :math:`q` is the heat flux, :math:`k`\nis the thermal conductivity, and :math:`T` is the temperature.\n\n .. math::\n\n -\\nabla \\cdot (-k \\nabla T) = q\n\nIt is also possible to run transient heat simulations by specifying ``analysis_spec=UnsteadyHeatAnalysis(...)``. This adds\nthe temporal terms to the above equations:\n\n .. math::\n\n \\frac{\\partial \\rho c_p T}{\\partial t} -\\nabla \\cdot (k \\nabla(T)) = q\n\nwhere :math:`\\rho` is the density and :math:`c_p` is the specific heat capacity of the medium.\n\n\nThe steady-state electrical ``Conduction`` equation depends on the electric conductivity (:math:`\\sigma`) of a\nmedium, and the electric field (:math:`\\mathbf{E} = -\\nabla(\\psi)`) derived from electrical potential (:math:`\\psi`).\nCurrently, in this type of simulation, no current sources or sinks are supported.\n\n .. math::\n\n \\text{div}(\\sigma \\cdot \\nabla(\\psi)) = 0\n\n\nFor further details on what equations are solved in ``Charge`` simulations, refer to the :class:`SemiconductorMedium`.\n\nLet's understand how the physics solving is determined:\n\n .. list-table::\n :widths: 25 75\n :header-rows: 1\n\n * - Simulation Type\n - Example Configuration Settings\n * - ``Heat``\n - The heat equation is solved with specified heat sources,\n boundary conditions, etc. Structures should incorporate materials\n with defined heat properties.\n * - ``Conduction``\n - The electrical conduction equation is solved with\n specified boundary conditions such as ``SteadyVoltageBC``, ``SteadyCurrentBC``, ...\n * - ``Charge``\n - Drift-diffusion equations are solved for structures containing\n a defined :class:`SemiconductorMedium`. Insulators with a\n :class:`ChargeInsulatorMedium` can also be included. For these, only the\n electric potential field is calculated.\n\nExamples\n--------\nTo run a thermal (``Heat`` |:fire:|) simulation with a solid conductive structure:\n\n>>> import tidy3d as td\n>>> heat_sim = td.HeatChargeSimulation(\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0,\n... heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )\n\nTo run a drift-diffusion (``Charge`` |:zap:|) system:\n\n>>> import tidy3d as td\n>>> air = td.FluidMedium(\n... name=\"air\"\n... )\n>>> intrinsic_Si = td.material_library['cSi'].variants['Si_MultiPhysics'].medium.charge\n>>> Si_n = intrinsic_Si.updated_copy(N_d=1e16, name=\"Si_n\")\n>>> Si_p = intrinsic_Si.updated_copy(N_a=1e16, name=\"Si_p\")\n>>> n_side = td.Structure(\n... geometry=td.Box(center=(-0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_n,\n... name=\"n_side\"\n... )\n>>> p_side = td.Structure(\n... geometry=td.Box(center=(0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_p,\n... name=\"p_side\"\n... )\n>>> bc_v1 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=[-1, 0, 0.5])),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_n.name]),\n... )\n>>> bc_v2 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=0)),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_p.name]),\n... )\n>>> charge_sim = td.HeatChargeSimulation(\n... structures=[n_side, p_side],\n... medium=td.Medium(heat_spec=td.FluidSpec(), name=\"air\"),\n... monitors=[td.SteadyFreeCarrierMonitor(\n... center=(0, 0, 0), size=(td.inf, td.inf, 0), name=\"charge_mnt\", unstructured=True\n... )],\n... center=(0, 0, 0),\n... size=(3, 3, 3),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.05),\n... boundary_spec=[bc_v1, bc_v2],\n... analysis_spec=td.IsothermalSteadyChargeDCAnalysis(\n... tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=3e3, max_iters=400),\n... convergence_dv=10),\n... )\n\n\nCoupling between ``Heat`` and electrical ``Conduction`` simulations is currently limited to 1-way.\nThis is specified by defining a heat source of type :class:`HeatFromElectricSource`. With this coupling, joule heating is\ncalculated as part of the solution to a ``Conduction`` simulation and translated into the ``Heat`` simulation.\n\nTwo common scenarios can use this coupling definition:\n 1. One in which BCs and sources are specified for both ``Heat`` and ``Conduction`` simulations.\n In this case one mesh will be generated and used for both the ``Conduction`` and ``Heat``\n simulations.\n 2. Only heat BCs/sources are provided. In this case, only the ``Heat`` equation will be solved.\n Before the simulation starts, it will try to load the heat source from file so a\n previously run ``Conduction`` simulations must have run previously. Since the Conduction\n and ``Heat`` meshes may differ, an interpolation between them will be performed prior to\n starting the ``Heat`` simulation.\n\nAdditional heat sources can be defined, in which case, they will be added on\ntop of the coupling heat source.", + "description": "Defines thermoelectric simulations.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nNotes\n-----\n A ``HeatChargeSimulation`` supports different types of simulations. It solves the\n heat and conduction equations using the Finite-Volume (FV) method. This solver\n determines the required computation physics according to the simulation scene definition.\n This is implemented in this way due to the strong multi-physics coupling.\n\nThe ``HeatChargeSimulation`` can solve multiple physics and the intention is to enable close thermo-electrical coupling.\n\nCurrently, this solver supports steady-state heat conduction where :math:`q` is the heat flux, :math:`k`\nis the thermal conductivity, and :math:`T` is the temperature.\n\n .. math::\n\n -\\nabla \\cdot (-k \\nabla T) = q\n\nIt is also possible to run transient heat simulations by specifying ``analysis_spec=UnsteadyHeatAnalysis(...)``. This adds\nthe temporal terms to the above equations:\n\n .. math::\n\n \\frac{\\partial \\rho c_p T}{\\partial t} -\\nabla \\cdot (k \\nabla(T)) = q\n\nwhere :math:`\\rho` is the density and :math:`c_p` is the specific heat capacity of the medium.\n\n\nThe steady-state electrical ``Conduction`` equation depends on the electric conductivity (:math:`\\sigma`) of a\nmedium, and the electric field (:math:`\\mathbf{E} = -\\nabla(\\psi)`) derived from electrical potential (:math:`\\psi`).\nCurrently, in this type of simulation, no current sources or sinks are supported.\n\n .. math::\n\n \\text{div}(\\sigma \\cdot \\nabla(\\psi)) = 0\n\n\nFor further details on what equations are solved in ``Charge`` simulations, refer to the :class:`SemiconductorMedium`.\n\nLet's understand how the physics solving is determined:\n\n .. list-table::\n :widths: 25 75\n :header-rows: 1\n\n * - Simulation Type\n - Example Configuration Settings\n * - ``Heat``\n - The heat equation is solved with specified heat sources,\n boundary conditions, etc. Structures should incorporate materials\n with defined heat properties.\n * - ``Conduction``\n - The electrical conduction equation is solved with\n specified boundary conditions such as ``SteadyVoltageBC``, ``SteadyCurrentBC``, ...\n * - ``Charge``\n - Drift-diffusion equations are solved for structures containing\n a defined :class:`SemiconductorMedium`. Insulators with a\n :class:`ChargeInsulatorMedium` can also be included. For these, only the\n electric potential field is calculated.\n\nExamples\n--------\nTo run a thermal (``Heat`` |:fire:|) simulation with a solid conductive structure:\n\n>>> import tidy3d as td\n>>> heat_sim = td.HeatChargeSimulation(\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0,\n... heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )\n\nTo run a drift-diffusion (``Charge`` |:zap:|) system:\n\n>>> import tidy3d as td\n>>> air = td.FluidMedium(\n... name=\"air\"\n... )\n>>> intrinsic_Si = td.material_library['cSi'].variants['Si_MultiPhysics'].medium.charge\n>>> Si_n = intrinsic_Si.updated_copy(N_d=1e16, name=\"Si_n\")\n>>> Si_p = intrinsic_Si.updated_copy(N_a=1e16, name=\"Si_p\")\n>>> n_side = td.Structure(\n... geometry=td.Box(center=(-0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_n,\n... name=\"n_side\"\n... )\n>>> p_side = td.Structure(\n... geometry=td.Box(center=(0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_p,\n... name=\"p_side\"\n... )\n>>> bc_v1 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=[-1, 0, 0.5])),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_n.name]),\n... )\n>>> bc_v2 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=0)),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_p.name]),\n... )\n>>> charge_sim = td.HeatChargeSimulation(\n... structures=[n_side, p_side],\n... medium=td.Medium(heat_spec=td.FluidSpec(), name=\"air\"),\n... monitors=[td.SteadyFreeCarrierMonitor(\n... center=(0, 0, 0), size=(td.inf, td.inf, 0), name=\"charge_mnt\", unstructured=True\n... )],\n... center=(0, 0, 0),\n... size=(3, 3, 3),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.05),\n... boundary_spec=[bc_v1, bc_v2],\n... analysis_spec=td.IsothermalSteadyChargeDCAnalysis(\n... tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=3e3, max_iters=400),\n... convergence_dv=10),\n... )\n\n\nCoupling between ``Heat`` and electrical ``Conduction`` simulations is currently limited to 1-way.\nThis is specified by defining a heat source of type :class:`HeatFromElectricSource`. With this coupling, joule heating is\ncalculated as part of the solution to a ``Conduction`` simulation and translated into the ``Heat`` simulation.\n\nTwo common scenarios can use this coupling definition:\n 1. One in which BCs and sources are specified for both ``Heat`` and ``Conduction`` simulations.\n In this case one mesh will be generated and used for both the ``Conduction`` and ``Heat``\n simulations.\n 2. Only heat BCs/sources are provided. In this case, only the ``Heat`` equation will be solved.\n Before the simulation starts, it will try to load the heat source from file so a\n previously run ``Conduction`` simulations must have run previously. Since the Conduction\n and ``Heat`` meshes may differ, an interpolation between them will be performed prior to\n starting the ``Heat`` simulation.\n\nAdditional heat sources can be defined, in which case, they will be added on\ntop of the coupling heat source.", "type": "object", "properties": { "attrs": { @@ -426,7 +426,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { diff --git a/schemas/HeatSimulation.json b/schemas/HeatSimulation.json index 89d2d5d793..2f42b1d23c 100644 --- a/schemas/HeatSimulation.json +++ b/schemas/HeatSimulation.json @@ -1,6 +1,6 @@ { "title": "HeatSimulation", - "description": "Contains all information about heat simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nExample\n-------\n>>> import tidy3d as td\n>>> heat_sim = td.HeatSimulation( # doctest: +SKIP\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0, heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )", + "description": "Contains all information about heat simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nExample\n-------\n>>> import tidy3d as td\n>>> heat_sim = td.HeatSimulation( # doctest: +SKIP\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0, heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )", "type": "object", "properties": { "attrs": { @@ -426,7 +426,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { diff --git a/schemas/ModeSimulation.json b/schemas/ModeSimulation.json index 45744c16e5..34e1aa58e2 100644 --- a/schemas/ModeSimulation.json +++ b/schemas/ModeSimulation.json @@ -1,6 +1,6 @@ { "title": "ModeSimulation", - "description": "Simulation class for solving electromagnetic eigenmodes in a 2D plane with\ntranslational invariance in the third dimension. If the field ``plane`` is\nspecified, the domain for mode solving is the intersection between the ``plane``\nand the simulation geometry. If the simulation geometry is 2D (has zero size\nin one dimension) and the ``plane`` is ``None``, then the domain for mode solving\nis the entire 2D geometry.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[] = ()\n Sources in the simulation. Note: sources are not supported in mode simulations.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides. This behavior is for consistency with FDTD simulations; however, please note that the mode solver terminates the mode plane with PEC boundary. The 'ModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[PermittivityMonitor, ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nmode_spec : ModeSpec\n Container with specifications about the modes to be solved for.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n A list of frequencies at which to solve.\ndirection : Literal['+', '-'] = +\n Direction of waveguide mode propagation along the axis defined by its normal dimension.\ncolocate : bool = True\n Toggle whether fields should be colocated to grid cell boundaries (i.e. primal grid nodes). Default is ``True``.\nfields : Tuple[Literal['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz'], ...] = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz']\n Collection of field components to store in the monitor. Note that some methods like ``flux``, ``dot`` require all tangential field components, while others like ``mode_area`` require all E-field components.\nplane : Union[Box, ModeSource, ModeMonitor, ModeSolverMonitor] = None\n Cross-sectional plane in which the mode will be computed. If provided, the computational domain will be the intersection between the provided ``plane`` and the simulation geometry. If ``None``, the simulation must be 2D, and the plane will be the entire simulation geometry.\n\nThe ``symmetry`` field can be used to enforce reflection symmetry across planes\nthrough the ``center`` of the simulation. Each component of the ``symmetry`` field\nis only used if the ``center`` of the ``plane`` and the simulation geometry\ncoincide in that component. Symmetry normal to the mode solving domain has no\neffect; the field ``filter_pol`` in :class:`.ModeSpec` can be used here instead.\n\nExample\n-------\n>>> from tidy3d import C_0, ModeSpec, BoundarySpec, Boundary\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> freqs = [freq0]\n>>> sim_size = lambda0, lambda0, 0\n>>> mode_spec = ModeSpec(num_modes=4)\n>>> boundary_spec = BoundarySpec(\n... x=Boundary.pec(),\n... y=Boundary.pec(),\n... z=Boundary.periodic()\n... )\n>>> sim = ModeSimulation(\n... size=sim_size,\n... freqs=freqs,\n... mode_spec=mode_spec,\n... boundary_spec=boundary_spec\n... )\n\nSee Also\n--------\n\n:class:`ModeSource`:\n Injects current source to excite modal profile on finite extent plane.\n\n**Notebooks:**\n * `Waveguide Y junction <../../notebooks/YJunction.html>`_\n * `Photonic crystal waveguide polarization filter <../../../notebooks/PhotonicCrystalWaveguidePolarizationFilter.html>`_\n\n**Lectures:**\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_", + "description": "Simulation class for solving electromagnetic eigenmodes in a 2D plane with\ntranslational invariance in the third dimension. If the field ``plane`` is\nspecified, the domain for mode solving is the intersection between the ``plane``\nand the simulation geometry. If the simulation geometry is 2D (has zero size\nin one dimension) and the ``plane`` is ``None``, then the domain for mode solving\nis the entire 2D geometry.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[] = ()\n Sources in the simulation. Note: sources are not supported in mode simulations.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides. This behavior is for consistency with FDTD simulations; however, please note that the mode solver terminates the mode plane with PEC boundary. The 'ModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[PermittivityMonitor, ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nmode_spec : ModeSpec\n Container with specifications about the modes to be solved for.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n A list of frequencies at which to solve.\ndirection : Literal['+', '-'] = +\n Direction of waveguide mode propagation along the axis defined by its normal dimension.\ncolocate : bool = True\n Toggle whether fields should be colocated to grid cell boundaries (i.e. primal grid nodes). Default is ``True``.\nfields : Tuple[Literal['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz'], ...] = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz']\n Collection of field components to store in the monitor. Note that some methods like ``flux``, ``dot`` require all tangential field components, while others like ``mode_area`` require all E-field components.\nplane : Union[Box, ModeSource, ModeMonitor, ModeSolverMonitor] = None\n Cross-sectional plane in which the mode will be computed. If provided, the computational domain will be the intersection between the provided ``plane`` and the simulation geometry. If ``None``, the simulation must be 2D, and the plane will be the entire simulation geometry.\n\nThe ``symmetry`` field can be used to enforce reflection symmetry across planes\nthrough the ``center`` of the simulation. Each component of the ``symmetry`` field\nis only used if the ``center`` of the ``plane`` and the simulation geometry\ncoincide in that component. Symmetry normal to the mode solving domain has no\neffect; the field ``filter_pol`` in :class:`.ModeSpec` can be used here instead.\n\nExample\n-------\n>>> from tidy3d import C_0, ModeSpec, BoundarySpec, Boundary\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> freqs = [freq0]\n>>> sim_size = lambda0, lambda0, 0\n>>> mode_spec = ModeSpec(num_modes=4)\n>>> boundary_spec = BoundarySpec(\n... x=Boundary.pec(),\n... y=Boundary.pec(),\n... z=Boundary.periodic()\n... )\n>>> sim = ModeSimulation(\n... size=sim_size,\n... freqs=freqs,\n... mode_spec=mode_spec,\n... boundary_spec=boundary_spec\n... )\n\nSee Also\n--------\n\n:class:`ModeSource`:\n Injects current source to excite modal profile on finite extent plane.\n\n**Notebooks:**\n * `Waveguide Y junction <../../notebooks/YJunction.html>`_\n * `Photonic crystal waveguide polarization filter <../../../notebooks/PhotonicCrystalWaveguidePolarizationFilter.html>`_\n\n**Lectures:**\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_", "type": "object", "properties": { "attrs": { @@ -490,7 +490,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { diff --git a/schemas/Simulation.json b/schemas/Simulation.json index be73014743..4b876e9506 100644 --- a/schemas/Simulation.json +++ b/schemas/Simulation.json @@ -1,6 +1,6 @@ { "title": "Simulation", - "description": "Custom implementation of Maxwell\u2019s equations which represents the physical model to be solved using the FDTD\nmethod.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (no symmetry), ``1`` (even, i.e. 'PMC' symmetry) or ``-1`` (odd, i.e. 'PEC' symmetry). Note that the vectorial nature of the fields must be taken into account to correctly determine the symmetry value.\nsources : Tuple[Annotated[Union[tidy3d.components.source.current.UniformCurrentSource, tidy3d.components.source.current.PointDipole, tidy3d.components.source.field.GaussianBeam, tidy3d.components.source.field.AstigmaticGaussianBeam, tidy3d.components.source.field.ModeSource, tidy3d.components.source.field.PlaneWave, tidy3d.components.source.field.CustomFieldSource, tidy3d.components.source.current.CustomCurrentSource, tidy3d.components.source.field.TFSF], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of electric current sources injecting fields into the simulation.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides.\nmonitors : Tuple[Annotated[Union[tidy3d.components.monitor.FieldMonitor, tidy3d.components.monitor.FieldTimeMonitor, tidy3d.components.monitor.AuxFieldTimeMonitor, tidy3d.components.monitor.PermittivityMonitor, tidy3d.components.monitor.FluxMonitor, tidy3d.components.monitor.FluxTimeMonitor, tidy3d.components.monitor.ModeMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.FieldProjectionAngleMonitor, tidy3d.components.monitor.FieldProjectionCartesianMonitor, tidy3d.components.monitor.FieldProjectionKSpaceMonitor, tidy3d.components.monitor.DiffractionMonitor, tidy3d.components.monitor.DirectivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. \nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\ncourant : ConstrainedFloatValue = 0.99\n Normalized Courant stability factor that is no larger than 1 when CFL stability condition is met. It controls time step to spatial step ratio. Lower values lead to more stable simulations for dispersive materials, but result in longer simulation times.\nprecision : Literal['hybrid', 'double'] = hybrid\n Floating point precision to use in the computations.\nnormalize_index : Optional[NonNegativeInt] = 0\n Index of the source in the tuple of sources whose spectrum will be used to normalize the frequency-dependent data. If ``None``, the raw field data is returned unnormalized.\nshutoff : NonNegativeFloat = 1e-05\n Ratio of the instantaneous integrated E-field intensity to the maximum value at which the simulation will automatically terminate time stepping. Used to prevent extraneous run time of simulations with fully decayed fields. Set to ``0`` to disable this feature.\nrun_time : Union[PositiveFloat, RunTimeSpec]\n [units = sec]. Total electromagnetic evolution time in seconds. Note: If simulation 'shutoff' is specified, simulation will terminate early when shutoff condition met. Alternatively, user may supply a :class:`RunTimeSpec` to this field, which will auto-compute the ``run_time`` based on the contents of the spec. If this option is used, the evaluated ``run_time`` value is available in the ``Simulation._run_time`` property.\n\nNotes\n-----\n\n A ``Simulation`` defines a custom implementation of Maxwell's equations which represents the physical model\n to be solved using `the Finite-Difference Time-Domain (FDTD) method\n `_. ``tidy3d`` simulations\n run very quickly in the cloud through GPU parallelization.\n\n .. image:: ../../_static/img/field_update_fdtd.png\n :width: 50%\n :align: left\n\n FDTD is a method for simulating the interaction of electromagnetic waves with structures and materials. It is\n the most widely used method in photonics design. The Maxwell's\n equations implemented in the ``Simulation`` are solved per time-step in the order shown in this image.\n\n The simplified input to FDTD solver consists of the permittivity distribution defined by :attr:`structures`\n which describe the device and :attr:`sources` of electromagnetic excitation. This information is used to\n computate the time dynamics of the electric and magnetic fields in this system. From these time-domain\n results, frequency-domain information of the simulation can also be extracted, and used for device design and\n optimization.\n\n If you are new to the FDTD method, we recommend you get started with the `FDTD 101 Lecture Series\n `_\n\n **Dimensions Selection**\n\n By default, simulations are defined as 3D. To make the simulation 2D, we can just set the simulation\n :attr:`size` in one of the dimensions to be 0. However, note that we still have to define a grid size (eg.\n ``tidy3d.Simulation(size=[size_x, size_y, 0])``) and specify a periodic boundary condition in that direction.\n\n .. TODO sort out inheritance problem https://aware-moon.cloudvent.net/tidy3d/examples/notebooks/RingResonator/\n\n See further parameter explanations below.\n\nExample\n-------\n>>> from tidy3d import Sphere, Cylinder, PolySlab\n>>> from tidy3d import UniformCurrentSource, GaussianPulse\n>>> from tidy3d import FieldMonitor, FluxMonitor\n>>> from tidy3d import GridSpec, AutoGrid\n>>> from tidy3d import BoundarySpec, Boundary\n>>> from tidy3d import Medium\n>>> sim = Simulation(\n... size=(3.0, 3.0, 3.0),\n... grid_spec=GridSpec(\n... grid_x = AutoGrid(min_steps_per_wvl = 20),\n... grid_y = AutoGrid(min_steps_per_wvl = 20),\n... grid_z = AutoGrid(min_steps_per_wvl = 20)\n... ),\n... run_time=40e-11,\n... structures=[\n... Structure(\n... geometry=Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=Medium(permittivity=2.0),\n... ),\n... ],\n... sources=[\n... UniformCurrentSource(\n... size=(0, 0, 0),\n... center=(0, 0.5, 0),\n... polarization=\"Hx\",\n... source_time=GaussianPulse(\n... freq0=2e14,\n... fwidth=4e13,\n... ),\n... )\n... ],\n... monitors=[\n... FluxMonitor(size=(1, 1, 0), center=(0, 0, 0), freqs=[2e14, 2.5e14], name='flux'),\n... ],\n... symmetry=(0, 0, 0),\n... boundary_spec=BoundarySpec(\n... x = Boundary.pml(num_layers=20),\n... y = Boundary.pml(num_layers=30),\n... z = Boundary.periodic(),\n... ),\n... shutoff=1e-6,\n... courant=0.8,\n... subpixel=False,\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `Quickstart <../../notebooks/StartHere.html>`_: Usage in a basic simulation flow.\n * `Using automatic nonuniform meshing <../../notebooks/AutoGrid.html>`_\n * See nearly all notebooks for :class:`Simulation` applications.\n\n**Lectures:**\n * `Introduction to FDTD Simulation `_: Usage in a basic simulation flow.\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_\n\n**GUI:**\n * `FDTD Walkthrough `_", + "description": "Custom implementation of Maxwell\u2019s equations which represents the physical model to be solved using the FDTD\nmethod.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (no symmetry), ``1`` (even, i.e. 'PMC' symmetry) or ``-1`` (odd, i.e. 'PEC' symmetry). Note that the vectorial nature of the fields must be taken into account to correctly determine the symmetry value.\nsources : Tuple[Annotated[Union[tidy3d.components.source.current.UniformCurrentSource, tidy3d.components.source.current.PointDipole, tidy3d.components.source.field.GaussianBeam, tidy3d.components.source.field.AstigmaticGaussianBeam, tidy3d.components.source.field.ModeSource, tidy3d.components.source.field.PlaneWave, tidy3d.components.source.field.CustomFieldSource, tidy3d.components.source.current.CustomCurrentSource, tidy3d.components.source.field.TFSF], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of electric current sources injecting fields into the simulation.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides.\nmonitors : Tuple[Annotated[Union[tidy3d.components.monitor.FieldMonitor, tidy3d.components.monitor.FieldTimeMonitor, tidy3d.components.monitor.AuxFieldTimeMonitor, tidy3d.components.monitor.PermittivityMonitor, tidy3d.components.monitor.FluxMonitor, tidy3d.components.monitor.FluxTimeMonitor, tidy3d.components.monitor.ModeMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.FieldProjectionAngleMonitor, tidy3d.components.monitor.FieldProjectionCartesianMonitor, tidy3d.components.monitor.FieldProjectionKSpaceMonitor, tidy3d.components.monitor.DiffractionMonitor, tidy3d.components.monitor.DirectivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. \nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\ncourant : ConstrainedFloatValue = 0.99\n Normalized Courant stability factor that is no larger than 1 when CFL stability condition is met. It controls time step to spatial step ratio. Lower values lead to more stable simulations for dispersive materials, but result in longer simulation times.\nprecision : Literal['hybrid', 'double'] = hybrid\n Floating point precision to use in the computations.\nnormalize_index : Optional[NonNegativeInt] = 0\n Index of the source in the tuple of sources whose spectrum will be used to normalize the frequency-dependent data. If ``None``, the raw field data is returned unnormalized.\nshutoff : NonNegativeFloat = 1e-05\n Ratio of the instantaneous integrated E-field intensity to the maximum value at which the simulation will automatically terminate time stepping. Used to prevent extraneous run time of simulations with fully decayed fields. Set to ``0`` to disable this feature.\nrun_time : Union[PositiveFloat, RunTimeSpec]\n [units = sec]. Total electromagnetic evolution time in seconds. Note: If simulation 'shutoff' is specified, simulation will terminate early when shutoff condition met. Alternatively, user may supply a :class:`RunTimeSpec` to this field, which will auto-compute the ``run_time`` based on the contents of the spec. If this option is used, the evaluated ``run_time`` value is available in the ``Simulation._run_time`` property.\n\nNotes\n-----\n\n A ``Simulation`` defines a custom implementation of Maxwell's equations which represents the physical model\n to be solved using `the Finite-Difference Time-Domain (FDTD) method\n `_. ``tidy3d`` simulations\n run very quickly in the cloud through GPU parallelization.\n\n .. image:: ../../_static/img/field_update_fdtd.png\n :width: 50%\n :align: left\n\n FDTD is a method for simulating the interaction of electromagnetic waves with structures and materials. It is\n the most widely used method in photonics design. The Maxwell's\n equations implemented in the ``Simulation`` are solved per time-step in the order shown in this image.\n\n The simplified input to FDTD solver consists of the permittivity distribution defined by :attr:`structures`\n which describe the device and :attr:`sources` of electromagnetic excitation. This information is used to\n computate the time dynamics of the electric and magnetic fields in this system. From these time-domain\n results, frequency-domain information of the simulation can also be extracted, and used for device design and\n optimization.\n\n If you are new to the FDTD method, we recommend you get started with the `FDTD 101 Lecture Series\n `_\n\n **Dimensions Selection**\n\n By default, simulations are defined as 3D. To make the simulation 2D, we can just set the simulation\n :attr:`size` in one of the dimensions to be 0. However, note that we still have to define a grid size (eg.\n ``tidy3d.Simulation(size=[size_x, size_y, 0])``) and specify a periodic boundary condition in that direction.\n\n .. TODO sort out inheritance problem https://aware-moon.cloudvent.net/tidy3d/examples/notebooks/RingResonator/\n\n See further parameter explanations below.\n\nExample\n-------\n>>> from tidy3d import Sphere, Cylinder, PolySlab\n>>> from tidy3d import UniformCurrentSource, GaussianPulse\n>>> from tidy3d import FieldMonitor, FluxMonitor\n>>> from tidy3d import GridSpec, AutoGrid\n>>> from tidy3d import BoundarySpec, Boundary\n>>> from tidy3d import Medium\n>>> sim = Simulation(\n... size=(3.0, 3.0, 3.0),\n... grid_spec=GridSpec(\n... grid_x = AutoGrid(min_steps_per_wvl = 20),\n... grid_y = AutoGrid(min_steps_per_wvl = 20),\n... grid_z = AutoGrid(min_steps_per_wvl = 20)\n... ),\n... run_time=40e-11,\n... structures=[\n... Structure(\n... geometry=Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=Medium(permittivity=2.0),\n... ),\n... ],\n... sources=[\n... UniformCurrentSource(\n... size=(0, 0, 0),\n... center=(0, 0.5, 0),\n... polarization=\"Hx\",\n... source_time=GaussianPulse(\n... freq0=2e14,\n... fwidth=4e13,\n... ),\n... )\n... ],\n... monitors=[\n... FluxMonitor(size=(1, 1, 0), center=(0, 0, 0), freqs=[2e14, 2.5e14], name='flux'),\n... ],\n... symmetry=(0, 0, 0),\n... boundary_spec=BoundarySpec(\n... x = Boundary.pml(num_layers=20),\n... y = Boundary.pml(num_layers=30),\n... z = Boundary.periodic(),\n... ),\n... shutoff=1e-6,\n... courant=0.8,\n... subpixel=False,\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `Quickstart <../../notebooks/StartHere.html>`_: Usage in a basic simulation flow.\n * `Using automatic nonuniform meshing <../../notebooks/AutoGrid.html>`_\n * See nearly all notebooks for :class:`Simulation` applications.\n\n**Lectures:**\n * `Introduction to FDTD Simulation `_: Usage in a basic simulation flow.\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_\n\n**GUI:**\n * `FDTD Walkthrough `_", "type": "object", "properties": { "attrs": { @@ -591,7 +591,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { diff --git a/schemas/TerminalComponentModeler.json b/schemas/TerminalComponentModeler.json index 939db7ce95..2538ad2181 100644 --- a/schemas/TerminalComponentModeler.json +++ b/schemas/TerminalComponentModeler.json @@ -17367,7 +17367,7 @@ }, "Simulation": { "title": "Simulation", - "description": "Custom implementation of Maxwell\u2019s equations which represents the physical model to be solved using the FDTD\nmethod.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (no symmetry), ``1`` (even, i.e. 'PMC' symmetry) or ``-1`` (odd, i.e. 'PEC' symmetry). Note that the vectorial nature of the fields must be taken into account to correctly determine the symmetry value.\nsources : Tuple[Annotated[Union[tidy3d.components.source.current.UniformCurrentSource, tidy3d.components.source.current.PointDipole, tidy3d.components.source.field.GaussianBeam, tidy3d.components.source.field.AstigmaticGaussianBeam, tidy3d.components.source.field.ModeSource, tidy3d.components.source.field.PlaneWave, tidy3d.components.source.field.CustomFieldSource, tidy3d.components.source.current.CustomCurrentSource, tidy3d.components.source.field.TFSF], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of electric current sources injecting fields into the simulation.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides.\nmonitors : Tuple[Annotated[Union[tidy3d.components.monitor.FieldMonitor, tidy3d.components.monitor.FieldTimeMonitor, tidy3d.components.monitor.AuxFieldTimeMonitor, tidy3d.components.monitor.PermittivityMonitor, tidy3d.components.monitor.FluxMonitor, tidy3d.components.monitor.FluxTimeMonitor, tidy3d.components.monitor.ModeMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.FieldProjectionAngleMonitor, tidy3d.components.monitor.FieldProjectionCartesianMonitor, tidy3d.components.monitor.FieldProjectionKSpaceMonitor, tidy3d.components.monitor.DiffractionMonitor, tidy3d.components.monitor.DirectivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. \nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\ncourant : ConstrainedFloatValue = 0.99\n Normalized Courant stability factor that is no larger than 1 when CFL stability condition is met. It controls time step to spatial step ratio. Lower values lead to more stable simulations for dispersive materials, but result in longer simulation times.\nprecision : Literal['hybrid', 'double'] = hybrid\n Floating point precision to use in the computations.\nnormalize_index : Optional[NonNegativeInt] = 0\n Index of the source in the tuple of sources whose spectrum will be used to normalize the frequency-dependent data. If ``None``, the raw field data is returned unnormalized.\nshutoff : NonNegativeFloat = 1e-05\n Ratio of the instantaneous integrated E-field intensity to the maximum value at which the simulation will automatically terminate time stepping. Used to prevent extraneous run time of simulations with fully decayed fields. Set to ``0`` to disable this feature.\nrun_time : Union[PositiveFloat, RunTimeSpec]\n [units = sec]. Total electromagnetic evolution time in seconds. Note: If simulation 'shutoff' is specified, simulation will terminate early when shutoff condition met. Alternatively, user may supply a :class:`RunTimeSpec` to this field, which will auto-compute the ``run_time`` based on the contents of the spec. If this option is used, the evaluated ``run_time`` value is available in the ``Simulation._run_time`` property.\n\nNotes\n-----\n\n A ``Simulation`` defines a custom implementation of Maxwell's equations which represents the physical model\n to be solved using `the Finite-Difference Time-Domain (FDTD) method\n `_. ``tidy3d`` simulations\n run very quickly in the cloud through GPU parallelization.\n\n .. image:: ../../_static/img/field_update_fdtd.png\n :width: 50%\n :align: left\n\n FDTD is a method for simulating the interaction of electromagnetic waves with structures and materials. It is\n the most widely used method in photonics design. The Maxwell's\n equations implemented in the ``Simulation`` are solved per time-step in the order shown in this image.\n\n The simplified input to FDTD solver consists of the permittivity distribution defined by :attr:`structures`\n which describe the device and :attr:`sources` of electromagnetic excitation. This information is used to\n computate the time dynamics of the electric and magnetic fields in this system. From these time-domain\n results, frequency-domain information of the simulation can also be extracted, and used for device design and\n optimization.\n\n If you are new to the FDTD method, we recommend you get started with the `FDTD 101 Lecture Series\n `_\n\n **Dimensions Selection**\n\n By default, simulations are defined as 3D. To make the simulation 2D, we can just set the simulation\n :attr:`size` in one of the dimensions to be 0. However, note that we still have to define a grid size (eg.\n ``tidy3d.Simulation(size=[size_x, size_y, 0])``) and specify a periodic boundary condition in that direction.\n\n .. TODO sort out inheritance problem https://aware-moon.cloudvent.net/tidy3d/examples/notebooks/RingResonator/\n\n See further parameter explanations below.\n\nExample\n-------\n>>> from tidy3d import Sphere, Cylinder, PolySlab\n>>> from tidy3d import UniformCurrentSource, GaussianPulse\n>>> from tidy3d import FieldMonitor, FluxMonitor\n>>> from tidy3d import GridSpec, AutoGrid\n>>> from tidy3d import BoundarySpec, Boundary\n>>> from tidy3d import Medium\n>>> sim = Simulation(\n... size=(3.0, 3.0, 3.0),\n... grid_spec=GridSpec(\n... grid_x = AutoGrid(min_steps_per_wvl = 20),\n... grid_y = AutoGrid(min_steps_per_wvl = 20),\n... grid_z = AutoGrid(min_steps_per_wvl = 20)\n... ),\n... run_time=40e-11,\n... structures=[\n... Structure(\n... geometry=Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=Medium(permittivity=2.0),\n... ),\n... ],\n... sources=[\n... UniformCurrentSource(\n... size=(0, 0, 0),\n... center=(0, 0.5, 0),\n... polarization=\"Hx\",\n... source_time=GaussianPulse(\n... freq0=2e14,\n... fwidth=4e13,\n... ),\n... )\n... ],\n... monitors=[\n... FluxMonitor(size=(1, 1, 0), center=(0, 0, 0), freqs=[2e14, 2.5e14], name='flux'),\n... ],\n... symmetry=(0, 0, 0),\n... boundary_spec=BoundarySpec(\n... x = Boundary.pml(num_layers=20),\n... y = Boundary.pml(num_layers=30),\n... z = Boundary.periodic(),\n... ),\n... shutoff=1e-6,\n... courant=0.8,\n... subpixel=False,\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `Quickstart <../../notebooks/StartHere.html>`_: Usage in a basic simulation flow.\n * `Using automatic nonuniform meshing <../../notebooks/AutoGrid.html>`_\n * See nearly all notebooks for :class:`Simulation` applications.\n\n**Lectures:**\n * `Introduction to FDTD Simulation `_: Usage in a basic simulation flow.\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_\n\n**GUI:**\n * `FDTD Walkthrough `_", + "description": "Custom implementation of Maxwell\u2019s equations which represents the physical model to be solved using the FDTD\nmethod.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (no symmetry), ``1`` (even, i.e. 'PMC' symmetry) or ``-1`` (odd, i.e. 'PEC' symmetry). Note that the vectorial nature of the fields must be taken into account to correctly determine the symmetry value.\nsources : Tuple[Annotated[Union[tidy3d.components.source.current.UniformCurrentSource, tidy3d.components.source.current.PointDipole, tidy3d.components.source.field.GaussianBeam, tidy3d.components.source.field.AstigmaticGaussianBeam, tidy3d.components.source.field.ModeSource, tidy3d.components.source.field.PlaneWave, tidy3d.components.source.field.CustomFieldSource, tidy3d.components.source.current.CustomCurrentSource, tidy3d.components.source.field.TFSF], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of electric current sources injecting fields into the simulation.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides.\nmonitors : Tuple[Annotated[Union[tidy3d.components.monitor.FieldMonitor, tidy3d.components.monitor.FieldTimeMonitor, tidy3d.components.monitor.AuxFieldTimeMonitor, tidy3d.components.monitor.PermittivityMonitor, tidy3d.components.monitor.FluxMonitor, tidy3d.components.monitor.FluxTimeMonitor, tidy3d.components.monitor.ModeMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.FieldProjectionAngleMonitor, tidy3d.components.monitor.FieldProjectionCartesianMonitor, tidy3d.components.monitor.FieldProjectionKSpaceMonitor, tidy3d.components.monitor.DiffractionMonitor, tidy3d.components.monitor.DirectivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. \nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\ncourant : ConstrainedFloatValue = 0.99\n Normalized Courant stability factor that is no larger than 1 when CFL stability condition is met. It controls time step to spatial step ratio. Lower values lead to more stable simulations for dispersive materials, but result in longer simulation times.\nprecision : Literal['hybrid', 'double'] = hybrid\n Floating point precision to use in the computations.\nnormalize_index : Optional[NonNegativeInt] = 0\n Index of the source in the tuple of sources whose spectrum will be used to normalize the frequency-dependent data. If ``None``, the raw field data is returned unnormalized.\nshutoff : NonNegativeFloat = 1e-05\n Ratio of the instantaneous integrated E-field intensity to the maximum value at which the simulation will automatically terminate time stepping. Used to prevent extraneous run time of simulations with fully decayed fields. Set to ``0`` to disable this feature.\nrun_time : Union[PositiveFloat, RunTimeSpec]\n [units = sec]. Total electromagnetic evolution time in seconds. Note: If simulation 'shutoff' is specified, simulation will terminate early when shutoff condition met. Alternatively, user may supply a :class:`RunTimeSpec` to this field, which will auto-compute the ``run_time`` based on the contents of the spec. If this option is used, the evaluated ``run_time`` value is available in the ``Simulation._run_time`` property.\n\nNotes\n-----\n\n A ``Simulation`` defines a custom implementation of Maxwell's equations which represents the physical model\n to be solved using `the Finite-Difference Time-Domain (FDTD) method\n `_. ``tidy3d`` simulations\n run very quickly in the cloud through GPU parallelization.\n\n .. image:: ../../_static/img/field_update_fdtd.png\n :width: 50%\n :align: left\n\n FDTD is a method for simulating the interaction of electromagnetic waves with structures and materials. It is\n the most widely used method in photonics design. The Maxwell's\n equations implemented in the ``Simulation`` are solved per time-step in the order shown in this image.\n\n The simplified input to FDTD solver consists of the permittivity distribution defined by :attr:`structures`\n which describe the device and :attr:`sources` of electromagnetic excitation. This information is used to\n computate the time dynamics of the electric and magnetic fields in this system. From these time-domain\n results, frequency-domain information of the simulation can also be extracted, and used for device design and\n optimization.\n\n If you are new to the FDTD method, we recommend you get started with the `FDTD 101 Lecture Series\n `_\n\n **Dimensions Selection**\n\n By default, simulations are defined as 3D. To make the simulation 2D, we can just set the simulation\n :attr:`size` in one of the dimensions to be 0. However, note that we still have to define a grid size (eg.\n ``tidy3d.Simulation(size=[size_x, size_y, 0])``) and specify a periodic boundary condition in that direction.\n\n .. TODO sort out inheritance problem https://aware-moon.cloudvent.net/tidy3d/examples/notebooks/RingResonator/\n\n See further parameter explanations below.\n\nExample\n-------\n>>> from tidy3d import Sphere, Cylinder, PolySlab\n>>> from tidy3d import UniformCurrentSource, GaussianPulse\n>>> from tidy3d import FieldMonitor, FluxMonitor\n>>> from tidy3d import GridSpec, AutoGrid\n>>> from tidy3d import BoundarySpec, Boundary\n>>> from tidy3d import Medium\n>>> sim = Simulation(\n... size=(3.0, 3.0, 3.0),\n... grid_spec=GridSpec(\n... grid_x = AutoGrid(min_steps_per_wvl = 20),\n... grid_y = AutoGrid(min_steps_per_wvl = 20),\n... grid_z = AutoGrid(min_steps_per_wvl = 20)\n... ),\n... run_time=40e-11,\n... structures=[\n... Structure(\n... geometry=Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=Medium(permittivity=2.0),\n... ),\n... ],\n... sources=[\n... UniformCurrentSource(\n... size=(0, 0, 0),\n... center=(0, 0.5, 0),\n... polarization=\"Hx\",\n... source_time=GaussianPulse(\n... freq0=2e14,\n... fwidth=4e13,\n... ),\n... )\n... ],\n... monitors=[\n... FluxMonitor(size=(1, 1, 0), center=(0, 0, 0), freqs=[2e14, 2.5e14], name='flux'),\n... ],\n... symmetry=(0, 0, 0),\n... boundary_spec=BoundarySpec(\n... x = Boundary.pml(num_layers=20),\n... y = Boundary.pml(num_layers=30),\n... z = Boundary.periodic(),\n... ),\n... shutoff=1e-6,\n... courant=0.8,\n... subpixel=False,\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `Quickstart <../../notebooks/StartHere.html>`_: Usage in a basic simulation flow.\n * `Using automatic nonuniform meshing <../../notebooks/AutoGrid.html>`_\n * See nearly all notebooks for :class:`Simulation` applications.\n\n**Lectures:**\n * `Introduction to FDTD Simulation `_: Usage in a basic simulation flow.\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_\n\n**GUI:**\n * `FDTD Walkthrough `_", "type": "object", "properties": { "attrs": { @@ -17958,7 +17958,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { @@ -21488,7 +21488,7 @@ }, "HeatChargeSimulation": { "title": "HeatChargeSimulation", - "description": "Defines thermoelectric simulations.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nNotes\n-----\n A ``HeatChargeSimulation`` supports different types of simulations. It solves the\n heat and conduction equations using the Finite-Volume (FV) method. This solver\n determines the required computation physics according to the simulation scene definition.\n This is implemented in this way due to the strong multi-physics coupling.\n\nThe ``HeatChargeSimulation`` can solve multiple physics and the intention is to enable close thermo-electrical coupling.\n\nCurrently, this solver supports steady-state heat conduction where :math:`q` is the heat flux, :math:`k`\nis the thermal conductivity, and :math:`T` is the temperature.\n\n .. math::\n\n -\\nabla \\cdot (-k \\nabla T) = q\n\nIt is also possible to run transient heat simulations by specifying ``analysis_spec=UnsteadyHeatAnalysis(...)``. This adds\nthe temporal terms to the above equations:\n\n .. math::\n\n \\frac{\\partial \\rho c_p T}{\\partial t} -\\nabla \\cdot (k \\nabla(T)) = q\n\nwhere :math:`\\rho` is the density and :math:`c_p` is the specific heat capacity of the medium.\n\n\nThe steady-state electrical ``Conduction`` equation depends on the electric conductivity (:math:`\\sigma`) of a\nmedium, and the electric field (:math:`\\mathbf{E} = -\\nabla(\\psi)`) derived from electrical potential (:math:`\\psi`).\nCurrently, in this type of simulation, no current sources or sinks are supported.\n\n .. math::\n\n \\text{div}(\\sigma \\cdot \\nabla(\\psi)) = 0\n\n\nFor further details on what equations are solved in ``Charge`` simulations, refer to the :class:`SemiconductorMedium`.\n\nLet's understand how the physics solving is determined:\n\n .. list-table::\n :widths: 25 75\n :header-rows: 1\n\n * - Simulation Type\n - Example Configuration Settings\n * - ``Heat``\n - The heat equation is solved with specified heat sources,\n boundary conditions, etc. Structures should incorporate materials\n with defined heat properties.\n * - ``Conduction``\n - The electrical conduction equation is solved with\n specified boundary conditions such as ``SteadyVoltageBC``, ``SteadyCurrentBC``, ...\n * - ``Charge``\n - Drift-diffusion equations are solved for structures containing\n a defined :class:`SemiconductorMedium`. Insulators with a\n :class:`ChargeInsulatorMedium` can also be included. For these, only the\n electric potential field is calculated.\n\nExamples\n--------\nTo run a thermal (``Heat`` |:fire:|) simulation with a solid conductive structure:\n\n>>> import tidy3d as td\n>>> heat_sim = td.HeatChargeSimulation(\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0,\n... heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )\n\nTo run a drift-diffusion (``Charge`` |:zap:|) system:\n\n>>> import tidy3d as td\n>>> air = td.FluidMedium(\n... name=\"air\"\n... )\n>>> intrinsic_Si = td.material_library['cSi'].variants['Si_MultiPhysics'].medium.charge\n>>> Si_n = intrinsic_Si.updated_copy(N_d=1e16, name=\"Si_n\")\n>>> Si_p = intrinsic_Si.updated_copy(N_a=1e16, name=\"Si_p\")\n>>> n_side = td.Structure(\n... geometry=td.Box(center=(-0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_n,\n... name=\"n_side\"\n... )\n>>> p_side = td.Structure(\n... geometry=td.Box(center=(0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_p,\n... name=\"p_side\"\n... )\n>>> bc_v1 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=[-1, 0, 0.5])),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_n.name]),\n... )\n>>> bc_v2 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=0)),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_p.name]),\n... )\n>>> charge_sim = td.HeatChargeSimulation(\n... structures=[n_side, p_side],\n... medium=td.Medium(heat_spec=td.FluidSpec(), name=\"air\"),\n... monitors=[td.SteadyFreeCarrierMonitor(\n... center=(0, 0, 0), size=(td.inf, td.inf, 0), name=\"charge_mnt\", unstructured=True\n... )],\n... center=(0, 0, 0),\n... size=(3, 3, 3),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.05),\n... boundary_spec=[bc_v1, bc_v2],\n... analysis_spec=td.IsothermalSteadyChargeDCAnalysis(\n... tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=3e3, max_iters=400),\n... convergence_dv=10),\n... )\n\n\nCoupling between ``Heat`` and electrical ``Conduction`` simulations is currently limited to 1-way.\nThis is specified by defining a heat source of type :class:`HeatFromElectricSource`. With this coupling, joule heating is\ncalculated as part of the solution to a ``Conduction`` simulation and translated into the ``Heat`` simulation.\n\nTwo common scenarios can use this coupling definition:\n 1. One in which BCs and sources are specified for both ``Heat`` and ``Conduction`` simulations.\n In this case one mesh will be generated and used for both the ``Conduction`` and ``Heat``\n simulations.\n 2. Only heat BCs/sources are provided. In this case, only the ``Heat`` equation will be solved.\n Before the simulation starts, it will try to load the heat source from file so a\n previously run ``Conduction`` simulations must have run previously. Since the Conduction\n and ``Heat`` meshes may differ, an interpolation between them will be performed prior to\n starting the ``Heat`` simulation.\n\nAdditional heat sources can be defined, in which case, they will be added on\ntop of the coupling heat source.", + "description": "Defines thermoelectric simulations.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nNotes\n-----\n A ``HeatChargeSimulation`` supports different types of simulations. It solves the\n heat and conduction equations using the Finite-Volume (FV) method. This solver\n determines the required computation physics according to the simulation scene definition.\n This is implemented in this way due to the strong multi-physics coupling.\n\nThe ``HeatChargeSimulation`` can solve multiple physics and the intention is to enable close thermo-electrical coupling.\n\nCurrently, this solver supports steady-state heat conduction where :math:`q` is the heat flux, :math:`k`\nis the thermal conductivity, and :math:`T` is the temperature.\n\n .. math::\n\n -\\nabla \\cdot (-k \\nabla T) = q\n\nIt is also possible to run transient heat simulations by specifying ``analysis_spec=UnsteadyHeatAnalysis(...)``. This adds\nthe temporal terms to the above equations:\n\n .. math::\n\n \\frac{\\partial \\rho c_p T}{\\partial t} -\\nabla \\cdot (k \\nabla(T)) = q\n\nwhere :math:`\\rho` is the density and :math:`c_p` is the specific heat capacity of the medium.\n\n\nThe steady-state electrical ``Conduction`` equation depends on the electric conductivity (:math:`\\sigma`) of a\nmedium, and the electric field (:math:`\\mathbf{E} = -\\nabla(\\psi)`) derived from electrical potential (:math:`\\psi`).\nCurrently, in this type of simulation, no current sources or sinks are supported.\n\n .. math::\n\n \\text{div}(\\sigma \\cdot \\nabla(\\psi)) = 0\n\n\nFor further details on what equations are solved in ``Charge`` simulations, refer to the :class:`SemiconductorMedium`.\n\nLet's understand how the physics solving is determined:\n\n .. list-table::\n :widths: 25 75\n :header-rows: 1\n\n * - Simulation Type\n - Example Configuration Settings\n * - ``Heat``\n - The heat equation is solved with specified heat sources,\n boundary conditions, etc. Structures should incorporate materials\n with defined heat properties.\n * - ``Conduction``\n - The electrical conduction equation is solved with\n specified boundary conditions such as ``SteadyVoltageBC``, ``SteadyCurrentBC``, ...\n * - ``Charge``\n - Drift-diffusion equations are solved for structures containing\n a defined :class:`SemiconductorMedium`. Insulators with a\n :class:`ChargeInsulatorMedium` can also be included. For these, only the\n electric potential field is calculated.\n\nExamples\n--------\nTo run a thermal (``Heat`` |:fire:|) simulation with a solid conductive structure:\n\n>>> import tidy3d as td\n>>> heat_sim = td.HeatChargeSimulation(\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0,\n... heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )\n\nTo run a drift-diffusion (``Charge`` |:zap:|) system:\n\n>>> import tidy3d as td\n>>> air = td.FluidMedium(\n... name=\"air\"\n... )\n>>> intrinsic_Si = td.material_library['cSi'].variants['Si_MultiPhysics'].medium.charge\n>>> Si_n = intrinsic_Si.updated_copy(N_d=1e16, name=\"Si_n\")\n>>> Si_p = intrinsic_Si.updated_copy(N_a=1e16, name=\"Si_p\")\n>>> n_side = td.Structure(\n... geometry=td.Box(center=(-0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_n,\n... name=\"n_side\"\n... )\n>>> p_side = td.Structure(\n... geometry=td.Box(center=(0.5, 0, 0), size=(1, 1, 1)),\n... medium=Si_p,\n... name=\"p_side\"\n... )\n>>> bc_v1 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=[-1, 0, 0.5])),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_n.name]),\n... )\n>>> bc_v2 = td.HeatChargeBoundarySpec(\n... condition=td.VoltageBC(source=td.DCVoltageSource(voltage=0)),\n... placement=td.MediumMediumInterface(mediums=[air.name, Si_p.name]),\n... )\n>>> charge_sim = td.HeatChargeSimulation(\n... structures=[n_side, p_side],\n... medium=td.Medium(heat_spec=td.FluidSpec(), name=\"air\"),\n... monitors=[td.SteadyFreeCarrierMonitor(\n... center=(0, 0, 0), size=(td.inf, td.inf, 0), name=\"charge_mnt\", unstructured=True\n... )],\n... center=(0, 0, 0),\n... size=(3, 3, 3),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.05),\n... boundary_spec=[bc_v1, bc_v2],\n... analysis_spec=td.IsothermalSteadyChargeDCAnalysis(\n... tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=3e3, max_iters=400),\n... convergence_dv=10),\n... )\n\n\nCoupling between ``Heat`` and electrical ``Conduction`` simulations is currently limited to 1-way.\nThis is specified by defining a heat source of type :class:`HeatFromElectricSource`. With this coupling, joule heating is\ncalculated as part of the solution to a ``Conduction`` simulation and translated into the ``Heat`` simulation.\n\nTwo common scenarios can use this coupling definition:\n 1. One in which BCs and sources are specified for both ``Heat`` and ``Conduction`` simulations.\n In this case one mesh will be generated and used for both the ``Conduction`` and ``Heat``\n simulations.\n 2. Only heat BCs/sources are provided. In this case, only the ``Heat`` equation will be solved.\n Before the simulation starts, it will try to load the heat source from file so a\n previously run ``Conduction`` simulations must have run previously. Since the Conduction\n and ``Heat`` meshes may differ, an interpolation between them will be performed prior to\n starting the ``Heat`` simulation.\n\nAdditional heat sources can be defined, in which case, they will be added on\ntop of the coupling heat source.", "type": "object", "properties": { "attrs": { @@ -21914,7 +21914,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { @@ -21962,7 +21962,7 @@ }, "HeatSimulation": { "title": "HeatSimulation", - "description": "Contains all information about heat simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nExample\n-------\n>>> import tidy3d as td\n>>> heat_sim = td.HeatSimulation( # doctest: +SKIP\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0, heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )", + "description": "Contains all information about heat simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[MultiPhysicsMedium, Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium, Medium2D, AnisotropicMediumFromMedium2D, FluidSpec, SolidSpec, SolidMedium, FluidMedium, ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to a standard dispersion-less :class:`Medium` if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, 1], Literal[0, 1], Literal[0, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. Each element can be ``0`` (symmetry off) or ``1`` (symmetry on).\nsources : Tuple[Annotated[Union[tidy3d.components.tcad.source.heat.HeatSource, tidy3d.components.tcad.source.coupled.HeatFromElectricSource, tidy3d.components.tcad.source.heat.UniformHeatSource], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of heat and/or charge sources.\nboundary_spec : Tuple[Annotated[Union[tidy3d.components.tcad.boundary.specification.HeatChargeBoundarySpec, tidy3d.components.tcad.boundary.specification.HeatBoundarySpec], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n List of boundary condition specifications.\nmonitors : Tuple[Annotated[Union[tidy3d.components.tcad.monitors.heat.TemperatureMonitor, tidy3d.components.tcad.monitors.charge.SteadyPotentialMonitor, tidy3d.components.tcad.monitors.charge.SteadyFreeCarrierMonitor, tidy3d.components.tcad.monitors.charge.SteadyEnergyBandMonitor, tidy3d.components.tcad.monitors.charge.SteadyElectricFieldMonitor, tidy3d.components.tcad.monitors.charge.SteadyCapacitanceMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Monitors in the simulation.\ngrid_spec : Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]\n Grid specification for heat-charge simulation.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nanalysis_spec : Union[IsothermalSteadyChargeDCAnalysis, UnsteadyHeatAnalysis] = None\n The `analysis_spec` is used to specify the type of simulation. Currently, it is used to specify Charge simulations or transient Heat simulations.\n\nExample\n-------\n>>> import tidy3d as td\n>>> heat_sim = td.HeatSimulation( # doctest: +SKIP\n... size=(3.0, 3.0, 3.0),\n... structures=[\n... td.Structure(\n... geometry=td.Box(size=(1, 1, 1), center=(0, 0, 0)),\n... medium=td.Medium(\n... permittivity=2.0, heat_spec=td.SolidSpec(\n... conductivity=1,\n... capacity=1,\n... )\n... ),\n... name=\"box\",\n... ),\n... ],\n... medium=td.Medium(permittivity=3.0, heat_spec=td.FluidSpec()),\n... grid_spec=td.UniformUnstructuredGrid(dl=0.1),\n... sources=[td.HeatSource(rate=1, structures=[\"box\"])],\n... boundary_spec=[\n... td.HeatChargeBoundarySpec(\n... placement=td.StructureBoundary(structure=\"box\"),\n... condition=td.TemperatureBC(temperature=500),\n... )\n... ],\n... monitors=[td.TemperatureMonitor(size=(1, 2, 3), name=\"sample\")],\n... )", "type": "object", "properties": { "attrs": { @@ -22388,7 +22388,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { @@ -23549,7 +23549,7 @@ }, "EMESimulation": { "title": "EMESimulation", - "description": "EigenMode Expansion (EME) simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[NoneType, ...] = ()\n Sources in the simulation. NOTE: sources are not currently supported for EME simulations. Instead, the simulation performs full bidirectional propagation in the 'port_mode' basis. After running the simulation, use 'smatrix_in_basis' to use another set of modes or input field.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), y=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), z=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. By default, PEC boundary conditions are applied on all sides. This field is for consistency with FDTD simulations; however, please note that regardless of the 'boundary_spec', the mode solver terminates the mode plane with PEC boundary. The 'EMEModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[Annotated[Union[tidy3d.components.eme.monitor.EMEModeSolverMonitor, tidy3d.components.eme.monitor.EMEFieldMonitor, tidy3d.components.eme.monitor.EMECoefficientMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.PermittivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions. This is distinct from 'eme_grid_spec', which defines the 1D EME grid in the propagation direction.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n Frequencies for the EME simulation. The field is propagated independently at each provided frequency. This can be slow when the number of frequencies is large. In this case, consider using the approximate 'EMEFreqSweep' as the 'sweep_spec' instead of providing all desired frequencies here.\naxis : Literal[0, 1, 2]\n Propagation axis (0, 1, or 2) for the EME simulation.\neme_grid_spec : Union[EMEUniformGrid, EMECompositeGrid, EMEExplicitGrid]\n Specification for the EME propagation grid. The simulation is divided into cells in the propagation direction; this parameter specifies the layout of those cells. Mode solving is performed in each cell, and then propagation between cells is performed to determine the complete solution. This is distinct from 'grid_spec', which defines the grid in the two tangential directions, as well as the grid used for field monitors.\nstore_port_modes : bool = True\n Whether to store the modes associated with the two ports. Required to find scattering matrix in basis besides the computational basis.\nnormalize : bool = True\n Whether to normalize the port modes to unity flux, thereby normalizing the scattering matrix and expansion coefficients.\nport_offsets : Tuple[NonNegativeFloat, NonNegativeFloat] = (0, 0)\n Offsets for the two ports, relative to the simulation bounds along the propagation axis.\nsweep_spec : Union[EMELengthSweep, EMEModeSweep, EMEFreqSweep, EMEPeriodicitySweep, NoneType] = None\n Specification for a parameter sweep to be performed during the EME propagation step. The results are stored in 'sim_data.smatrix'. Other simulation monitor data is not included in the sweep.\nconstraint : Optional[Literal['passive', 'unitary']] = passive\n Constraint for EME propagation, imposed at cell interfaces. A constraint of 'passive' means that energy can be dissipated but not created at interfaces. A constraint of 'unitary' means that energy is conserved at interfaces (but not necessarily within cells). The option 'none' may be faster for a large number of modes. The option 'passive' can serve as regularization for the field continuity requirement and give more physical results.\n\nNotes\n-----\n\n EME is a frequency-domain method for propagating the electromagnetic field along a\n specified axis. The method is well-suited for propagation of guided waves.\n The electromagnetic fields are expanded locally in the basis of eigenmodes of the\n waveguide; they are then propagated by imposing continuity conditions in this basis.\n\n The EME simulation is performed along the propagation axis ``axis`` at frequencies ``freqs``.\n The simulation is divided into cells along the propagation axis, as defined by\n ``eme_grid_spec``. Mode solving is performed at cell centers, and boundary conditions are\n imposed between cells. The EME ports are defined to be the boundaries of the first and last\n cell in the EME grid. These can be moved using ``port_offsets``.\n\n An EME simulation always computes the full scattering matrix of the structure.\n Additional data can be recorded by adding 'monitors' to the simulation.\n\n **Other Bases**\n\n By default, the scattering matrix is expressed in the basis of EME modes at the two ports. It is sometimes useful to use alternative bases. For example, in a waveguide splitter, we might want the scattering matrix in the basis of modes of the individual waveguides. The functions `smatrix_in_basis` and `field_in_basis` in :class:`.EMESimulationData` can be used for this purpose after the simulation has been run.\n\n **Frequency Sweeps**\n\n Frequency sweeps are supported by including multiple frequencies in the `freqs` field. However, our EME solver repeats the mode solving for each new frequency, so frequency sweeps involving a large number of frequencies can be slow and expensive. If a large number of frequencies are required, consider using our FDTD solver instead.\n\n **Passivity and Unitarity Constraints**\n\n Passivity and unitarity constraints can be imposed via the `constraint` field. These constraints are imposed at interfaces between cells, possibly at the expense of field continuity. Passivity means that the interface can only dissipate energy, and unitarity means the interface will conserve energy (energy may still be dissipated inside cells when the propagation constant is complex). Adding constraints can slow down the simulation significantly, especially for a large number of modes (more than 30 or 40).\n\n **Too Many Modes**\n\n It is important to use enough modes to capture the physics of the device and to ensure that the results have converged (see below). However, using too many modes can slow down the simulation and result in numerical issues. If too many modes are used, it is common to see a warning about invalid modes in the simulation log. While these modes are not included in the EME propagation, this can indicate some other issue with the setup, especially if the results have not converged. In this case, extending the simulation size in the transverse directions and increasing the grid resolution may help by creating more valid modes that can be used in convergence testing.\n\n **Mode Convergence Sweeps**\n\n It is a good idea to check that the number of modes is large enough by running a mode convergence sweep. This can be done using :class:`.EMEModeSweep`.\n\nExample\n-------\n>>> from tidy3d import Box, Medium, Structure, C_0, inf\n>>> from tidy3d import EMEModeSpec, EMEUniformGrid, GridSpec\n>>> from tidy3d import EMEFieldMonitor\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> sim_size = 3*lambda0, 3*lambda0, 3*lambda0\n>>> waveguide_size = (lambda0/2, lambda0, inf)\n>>> waveguide = Structure(\n... geometry=Box(center=(0,0,0), size=waveguide_size),\n... medium=Medium(permittivity=2)\n... )\n>>> eme_grid_spec = EMEUniformGrid(num_cells=5, mode_spec=EMEModeSpec(num_modes=10))\n>>> grid_spec = GridSpec(wavelength=lambda0)\n>>> field_monitor = EMEFieldMonitor(\n... size=(0, sim_size[1], sim_size[2]),\n... name=\"field_monitor\"\n... )\n>>> sim = EMESimulation(\n... size=sim_size,\n... monitors=[field_monitor],\n... structures=[waveguide],\n... axis=2,\n... freqs=[freq0],\n... eme_grid_spec=eme_grid_spec,\n... grid_spec=grid_spec\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `EME Solver Demonstration <../../notebooks/docs/features/eme.rst>`_", + "description": "EigenMode Expansion (EME) simulation.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[NoneType, ...] = ()\n Sources in the simulation. NOTE: sources are not currently supported for EME simulations. Instead, the simulation performs full bidirectional propagation in the 'port_mode' basis. After running the simulation, use 'smatrix_in_basis' to use another set of modes or input field.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), y=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), z=Boundary(attrs={},, plus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, minus=PECBoundary(attrs={},, name=None,, type='PECBoundary'),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. By default, PEC boundary conditions are applied on all sides. This field is for consistency with FDTD simulations; however, please note that regardless of the 'boundary_spec', the mode solver terminates the mode plane with PEC boundary. The 'EMEModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[Annotated[Union[tidy3d.components.eme.monitor.EMEModeSolverMonitor, tidy3d.components.eme.monitor.EMEFieldMonitor, tidy3d.components.eme.monitor.EMECoefficientMonitor, tidy3d.components.monitor.ModeSolverMonitor, tidy3d.components.monitor.PermittivityMonitor], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions. This is distinct from 'eme_grid_spec', which defines the 1D EME grid in the propagation direction.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n Frequencies for the EME simulation. The field is propagated independently at each provided frequency. This can be slow when the number of frequencies is large. In this case, consider using the approximate 'EMEFreqSweep' as the 'sweep_spec' instead of providing all desired frequencies here.\naxis : Literal[0, 1, 2]\n Propagation axis (0, 1, or 2) for the EME simulation.\neme_grid_spec : Union[EMEUniformGrid, EMECompositeGrid, EMEExplicitGrid]\n Specification for the EME propagation grid. The simulation is divided into cells in the propagation direction; this parameter specifies the layout of those cells. Mode solving is performed in each cell, and then propagation between cells is performed to determine the complete solution. This is distinct from 'grid_spec', which defines the grid in the two tangential directions, as well as the grid used for field monitors.\nstore_port_modes : bool = True\n Whether to store the modes associated with the two ports. Required to find scattering matrix in basis besides the computational basis.\nnormalize : bool = True\n Whether to normalize the port modes to unity flux, thereby normalizing the scattering matrix and expansion coefficients.\nport_offsets : Tuple[NonNegativeFloat, NonNegativeFloat] = (0, 0)\n Offsets for the two ports, relative to the simulation bounds along the propagation axis.\nsweep_spec : Union[EMELengthSweep, EMEModeSweep, EMEFreqSweep, EMEPeriodicitySweep, NoneType] = None\n Specification for a parameter sweep to be performed during the EME propagation step. The results are stored in 'sim_data.smatrix'. Other simulation monitor data is not included in the sweep.\nconstraint : Optional[Literal['passive', 'unitary']] = passive\n Constraint for EME propagation, imposed at cell interfaces. A constraint of 'passive' means that energy can be dissipated but not created at interfaces. A constraint of 'unitary' means that energy is conserved at interfaces (but not necessarily within cells). The option 'none' may be faster for a large number of modes. The option 'passive' can serve as regularization for the field continuity requirement and give more physical results.\n\nNotes\n-----\n\n EME is a frequency-domain method for propagating the electromagnetic field along a\n specified axis. The method is well-suited for propagation of guided waves.\n The electromagnetic fields are expanded locally in the basis of eigenmodes of the\n waveguide; they are then propagated by imposing continuity conditions in this basis.\n\n The EME simulation is performed along the propagation axis ``axis`` at frequencies ``freqs``.\n The simulation is divided into cells along the propagation axis, as defined by\n ``eme_grid_spec``. Mode solving is performed at cell centers, and boundary conditions are\n imposed between cells. The EME ports are defined to be the boundaries of the first and last\n cell in the EME grid. These can be moved using ``port_offsets``.\n\n An EME simulation always computes the full scattering matrix of the structure.\n Additional data can be recorded by adding 'monitors' to the simulation.\n\n **Other Bases**\n\n By default, the scattering matrix is expressed in the basis of EME modes at the two ports. It is sometimes useful to use alternative bases. For example, in a waveguide splitter, we might want the scattering matrix in the basis of modes of the individual waveguides. The functions `smatrix_in_basis` and `field_in_basis` in :class:`.EMESimulationData` can be used for this purpose after the simulation has been run.\n\n **Frequency Sweeps**\n\n Frequency sweeps are supported by including multiple frequencies in the `freqs` field. However, our EME solver repeats the mode solving for each new frequency, so frequency sweeps involving a large number of frequencies can be slow and expensive. If a large number of frequencies are required, consider using our FDTD solver instead.\n\n **Passivity and Unitarity Constraints**\n\n Passivity and unitarity constraints can be imposed via the `constraint` field. These constraints are imposed at interfaces between cells, possibly at the expense of field continuity. Passivity means that the interface can only dissipate energy, and unitarity means the interface will conserve energy (energy may still be dissipated inside cells when the propagation constant is complex). Adding constraints can slow down the simulation significantly, especially for a large number of modes (more than 30 or 40).\n\n **Too Many Modes**\n\n It is important to use enough modes to capture the physics of the device and to ensure that the results have converged (see below). However, using too many modes can slow down the simulation and result in numerical issues. If too many modes are used, it is common to see a warning about invalid modes in the simulation log. While these modes are not included in the EME propagation, this can indicate some other issue with the setup, especially if the results have not converged. In this case, extending the simulation size in the transverse directions and increasing the grid resolution may help by creating more valid modes that can be used in convergence testing.\n\n **Mode Convergence Sweeps**\n\n It is a good idea to check that the number of modes is large enough by running a mode convergence sweep. This can be done using :class:`.EMEModeSweep`.\n\nExample\n-------\n>>> from tidy3d import Box, Medium, Structure, C_0, inf\n>>> from tidy3d import EMEModeSpec, EMEUniformGrid, GridSpec\n>>> from tidy3d import EMEFieldMonitor\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> sim_size = 3*lambda0, 3*lambda0, 3*lambda0\n>>> waveguide_size = (lambda0/2, lambda0, inf)\n>>> waveguide = Structure(\n... geometry=Box(center=(0,0,0), size=waveguide_size),\n... medium=Medium(permittivity=2)\n... )\n>>> eme_grid_spec = EMEUniformGrid(num_cells=5, mode_spec=EMEModeSpec(num_modes=10))\n>>> grid_spec = GridSpec(wavelength=lambda0)\n>>> field_monitor = EMEFieldMonitor(\n... size=(0, sim_size[1], sim_size[2]),\n... name=\"field_monitor\"\n... )\n>>> sim = EMESimulation(\n... size=sim_size,\n... monitors=[field_monitor],\n... structures=[waveguide],\n... axis=2,\n... freqs=[freq0],\n... eme_grid_spec=eme_grid_spec,\n... grid_spec=grid_spec\n... )\n\nSee Also\n--------\n\n**Notebooks:**\n * `EME Solver Demonstration <../../notebooks/docs/features/eme.rst>`_", "type": "object", "properties": { "attrs": { @@ -23983,7 +23983,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { @@ -24361,7 +24361,7 @@ }, "ModeSimulation": { "title": "ModeSimulation", - "description": "Simulation class for solving electromagnetic eigenmodes in a 2D plane with\ntranslational invariance in the third dimension. If the field ``plane`` is\nspecified, the domain for mode solving is the intersection between the ``plane``\nand the simulation geometry. If the simulation geometry is 2D (has zero size\nin one dimension) and the ``plane`` is ``None``, then the domain for mode solving\nis the entire 2D geometry.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[] = ()\n Sources in the simulation. Note: sources are not supported in mode simulations.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides. This behavior is for consistency with FDTD simulations; however, please note that the mode solver terminates the mode plane with PEC boundary. The 'ModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[PermittivityMonitor, ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.0\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nmode_spec : ModeSpec\n Container with specifications about the modes to be solved for.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n A list of frequencies at which to solve.\ndirection : Literal['+', '-'] = +\n Direction of waveguide mode propagation along the axis defined by its normal dimension.\ncolocate : bool = True\n Toggle whether fields should be colocated to grid cell boundaries (i.e. primal grid nodes). Default is ``True``.\nfields : Tuple[Literal['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz'], ...] = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz']\n Collection of field components to store in the monitor. Note that some methods like ``flux``, ``dot`` require all tangential field components, while others like ``mode_area`` require all E-field components.\nplane : Union[Box, ModeSource, ModeMonitor, ModeSolverMonitor] = None\n Cross-sectional plane in which the mode will be computed. If provided, the computational domain will be the intersection between the provided ``plane`` and the simulation geometry. If ``None``, the simulation must be 2D, and the plane will be the entire simulation geometry.\n\nThe ``symmetry`` field can be used to enforce reflection symmetry across planes\nthrough the ``center`` of the simulation. Each component of the ``symmetry`` field\nis only used if the ``center`` of the ``plane`` and the simulation geometry\ncoincide in that component. Symmetry normal to the mode solving domain has no\neffect; the field ``filter_pol`` in :class:`.ModeSpec` can be used here instead.\n\nExample\n-------\n>>> from tidy3d import C_0, ModeSpec, BoundarySpec, Boundary\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> freqs = [freq0]\n>>> sim_size = lambda0, lambda0, 0\n>>> mode_spec = ModeSpec(num_modes=4)\n>>> boundary_spec = BoundarySpec(\n... x=Boundary.pec(),\n... y=Boundary.pec(),\n... z=Boundary.periodic()\n... )\n>>> sim = ModeSimulation(\n... size=sim_size,\n... freqs=freqs,\n... mode_spec=mode_spec,\n... boundary_spec=boundary_spec\n... )\n\nSee Also\n--------\n\n:class:`ModeSource`:\n Injects current source to excite modal profile on finite extent plane.\n\n**Notebooks:**\n * `Waveguide Y junction <../../notebooks/YJunction.html>`_\n * `Photonic crystal waveguide polarization filter <../../../notebooks/PhotonicCrystalWaveguidePolarizationFilter.html>`_\n\n**Lectures:**\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_", + "description": "Simulation class for solving electromagnetic eigenmodes in a 2D plane with\ntranslational invariance in the third dimension. If the field ``plane`` is\nspecified, the domain for mode solving is the intersection between the ``plane``\nand the simulation geometry. If the simulation geometry is 2D (has zero size\nin one dimension) and the ``plane`` is ``None``, then the domain for mode solving\nis the entire 2D geometry.\n\nParameters\n----------\nattrs : dict = {}\n Dictionary storing arbitrary metadata for a Tidy3D object. This dictionary can be freely used by the user for storing data without affecting the operation of Tidy3D as it is not used internally. Note that, unlike regular Tidy3D fields, ``attrs`` are mutable. For example, the following is allowed for setting an ``attr`` ``obj.attrs['foo'] = bar``. Also note that `Tidy3D`` will raise a ``TypeError`` if ``attrs`` contain objects that can not be serialized. One can check if ``attrs`` are serializable by calling ``obj.json()``.\ncenter : Union[tuple[Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box], Union[float, autograd.tracer.Box]], Box] = (0.0, 0.0, 0.0)\n [units = um]. Center of object in x, y, and z.\nsize : Union[tuple[Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box], Union[pydantic.v1.types.NonNegativeFloat, autograd.tracer.Box]], Box]\n [units = um]. Size in x, y, and z directions.\nmedium : Union[Medium, AnisotropicMedium, PECMedium, PMCMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, FullyAnisotropicMedium, CustomMedium, CustomPoleResidue, CustomSellmeier, CustomLorentz, CustomDebye, CustomDrude, CustomAnisotropicMedium, PerturbationMedium, PerturbationPoleResidue, LossyMetalMedium] = Medium(attrs={}, name=None, frequency_range=None, allow_gain=False, nonlinear_spec=None, modulation_spec=None, viz_spec=None, heat_spec=None, type='Medium', permittivity=1.0, conductivity=0.0)\n Background medium of simulation, defaults to vacuum if not specified.\nstructures : Tuple[Structure, ...] = ()\n Tuple of structures present in simulation. Note: Structures defined later in this list override the simulation material properties in regions of spatial overlap.\nsymmetry : Tuple[Literal[0, -1, 1], Literal[0, -1, 1], Literal[0, -1, 1]] = (0, 0, 0)\n Tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis at the simulation center of each axis, respectively. \nsources : Tuple[] = ()\n Sources in the simulation. Note: sources are not supported in mode simulations.\nboundary_spec : BoundarySpec = BoundarySpec(attrs={}, x=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), y=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), z=Boundary(attrs={},, plus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, minus=PML(attrs={},, name=None,, type='PML',, num_layers=12,, parameters=PMLParams(attrs={},, sigma_order=3,, sigma_min=0.0,, sigma_max=1.5,, type='PMLParams',, kappa_order=3,, kappa_min=1.0,, kappa_max=3.0,, alpha_order=1,, alpha_min=0.0,, alpha_max=0.0)),, type='Boundary'), type='BoundarySpec')\n Specification of boundary conditions along each dimension. If ``None``, PML boundary conditions are applied on all sides. This behavior is for consistency with FDTD simulations; however, please note that the mode solver terminates the mode plane with PEC boundary. The 'ModeSpec' can be used to apply PML layers in the mode solver.\nmonitors : Tuple[PermittivityMonitor, ...] = ()\n Tuple of monitors in the simulation. Note: monitor names are used to access data after simulation is run.\ngrid_spec : GridSpec = GridSpec(attrs={}, grid_x=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_y=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), grid_z=AutoGrid(attrs={},, type='AutoGrid',, max_scale=1.4,, mesher=GradedMesher(attrs={},, type='GradedMesher'),, dl_min=None,, min_steps_per_wvl=10.0,, min_steps_per_sim_size=10.0), wavelength=None, override_structures=(), snapping_points=(), layer_refinement_specs=(), type='GridSpec')\n Specifications for the simulation grid along each of the three directions.\nversion : str = 2.9.1\n String specifying the front end version number.\nplot_length_units : Optional[Literal['nm', '\u03bcm', 'um', 'mm', 'cm', 'm']] = \u03bcm\n When set to a supported ``LengthUnit``, plots will be produced with proper scaling of axes and include the desired unit specifier in labels.\nstructure_priority_mode : Literal['equal', 'conductor'] = equal\n This field only affects structures of `priority=None`. If `equal`, the priority of those structures is set to 0; if `conductor`, the priority of structures made of `LossyMetalMedium` is set to 90, `PECMedium` to 100, and others to 0.\nlumped_elements : Tuple[Annotated[Union[tidy3d.components.lumped_element.LumpedResistor, tidy3d.components.lumped_element.CoaxialLumpedResistor, tidy3d.components.lumped_element.LinearLumpedElement], FieldInfo(default=PydanticUndefined, discriminator='type', extra={})], ...] = ()\n Tuple of lumped elements in the simulation. Note: only :class:`tidy3d.LumpedResistor` is supported currently.\nsubpixel : Union[bool, SubpixelSpec] = SubpixelSpec(attrs={}, dielectric=PolarizedAveraging(attrs={},, type='PolarizedAveraging'), metal=Staircasing(attrs={},, type='Staircasing'), pec=PECConformal(attrs={},, type='PECConformal',, timestep_reduction=0.3,, edge_singularity_correction=False), pmc=Staircasing(attrs={},, type='Staircasing'), lossy_metal=SurfaceImpedance(attrs={},, type='SurfaceImpedance',, timestep_reduction=0.0,, edge_singularity_correction=False), type='SubpixelSpec')\n Apply subpixel averaging methods of the permittivity on structure interfaces to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` to this field to select subpixel averaging methods separately on dielectric, metal, and PEC material interfaces. Alternatively, user may supply a boolean value: ``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` , or ``False`` to apply staircasing.\nsimulation_type : Optional[Literal['autograd_fwd', 'autograd_bwd', 'tidy3d', None]] = tidy3d\n Tag used internally to distinguish types of simulations for ``autograd`` gradient processing.\npost_norm : Union[float, FreqDataArray] = 1.0\n Factor to multiply the fields by after running, given the adjoint source pipeline used. Note: this is used internally only.\nmode_spec : ModeSpec\n Container with specifications about the modes to be solved for.\nfreqs : Union[tuple[float, ...], ArrayLike[dtype=float, ndim=1]]\n A list of frequencies at which to solve.\ndirection : Literal['+', '-'] = +\n Direction of waveguide mode propagation along the axis defined by its normal dimension.\ncolocate : bool = True\n Toggle whether fields should be colocated to grid cell boundaries (i.e. primal grid nodes). Default is ``True``.\nfields : Tuple[Literal['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz'], ...] = ['Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz']\n Collection of field components to store in the monitor. Note that some methods like ``flux``, ``dot`` require all tangential field components, while others like ``mode_area`` require all E-field components.\nplane : Union[Box, ModeSource, ModeMonitor, ModeSolverMonitor] = None\n Cross-sectional plane in which the mode will be computed. If provided, the computational domain will be the intersection between the provided ``plane`` and the simulation geometry. If ``None``, the simulation must be 2D, and the plane will be the entire simulation geometry.\n\nThe ``symmetry`` field can be used to enforce reflection symmetry across planes\nthrough the ``center`` of the simulation. Each component of the ``symmetry`` field\nis only used if the ``center`` of the ``plane`` and the simulation geometry\ncoincide in that component. Symmetry normal to the mode solving domain has no\neffect; the field ``filter_pol`` in :class:`.ModeSpec` can be used here instead.\n\nExample\n-------\n>>> from tidy3d import C_0, ModeSpec, BoundarySpec, Boundary\n>>> lambda0 = 1550e-9\n>>> freq0 = C_0 / lambda0\n>>> freqs = [freq0]\n>>> sim_size = lambda0, lambda0, 0\n>>> mode_spec = ModeSpec(num_modes=4)\n>>> boundary_spec = BoundarySpec(\n... x=Boundary.pec(),\n... y=Boundary.pec(),\n... z=Boundary.periodic()\n... )\n>>> sim = ModeSimulation(\n... size=sim_size,\n... freqs=freqs,\n... mode_spec=mode_spec,\n... boundary_spec=boundary_spec\n... )\n\nSee Also\n--------\n\n:class:`ModeSource`:\n Injects current source to excite modal profile on finite extent plane.\n\n**Notebooks:**\n * `Waveguide Y junction <../../notebooks/YJunction.html>`_\n * `Photonic crystal waveguide polarization filter <../../../notebooks/PhotonicCrystalWaveguidePolarizationFilter.html>`_\n\n**Lectures:**\n * `Prelude to Integrated Photonics Simulation: Mode Injection `_", "type": "object", "properties": { "attrs": { @@ -24851,7 +24851,7 @@ "version": { "title": "Version", "description": "String specifying the front end version number.", - "default": "2.9.0", + "default": "2.9.1", "type": "string" }, "plot_length_units": { diff --git a/tests/sims/full_charge.h5 b/tests/sims/full_charge.h5 index 0191ac8ee6..2b357d3106 100644 Binary files a/tests/sims/full_charge.h5 and b/tests/sims/full_charge.h5 differ diff --git a/tests/sims/full_charge.json b/tests/sims/full_charge.json index 8f590eac4d..b396370dc5 100644 --- a/tests/sims/full_charge.json +++ b/tests/sims/full_charge.json @@ -377,7 +377,7 @@ "min_edges_per_side": 2.0, "non_refined_structures": [] }, - "version": "2.9.0", + "version": "2.9.1", "plot_length_units": "\u03bcm", "structure_priority_mode": "equal", "analysis_spec": { diff --git a/tests/sims/full_conduction.h5 b/tests/sims/full_conduction.h5 index 597ef6190d..26db47243a 100644 Binary files a/tests/sims/full_conduction.h5 and b/tests/sims/full_conduction.h5 differ diff --git a/tests/sims/full_conduction.json b/tests/sims/full_conduction.json index 3d40d8ba07..736890ca5f 100644 --- a/tests/sims/full_conduction.json +++ b/tests/sims/full_conduction.json @@ -277,7 +277,7 @@ "min_edges_per_side": 2.0, "non_refined_structures": [] }, - "version": "2.9.0", + "version": "2.9.1", "plot_length_units": "\u03bcm", "structure_priority_mode": "equal", "analysis_spec": null diff --git a/tests/sims/full_fdtd.h5 b/tests/sims/full_fdtd.h5 index d6711391b0..d4fb405089 100644 Binary files a/tests/sims/full_fdtd.h5 and b/tests/sims/full_fdtd.h5 differ diff --git a/tests/sims/full_fdtd.json b/tests/sims/full_fdtd.json index 77d57d4501..09015df5e8 100644 --- a/tests/sims/full_fdtd.json +++ b/tests/sims/full_fdtd.json @@ -3394,7 +3394,7 @@ "layer_refinement_specs": [], "type": "GridSpec" }, - "version": "2.9.0", + "version": "2.9.1", "plot_length_units": "\u03bcm", "structure_priority_mode": "equal", "lumped_elements": [ diff --git a/tests/sims/full_steady_heat.h5 b/tests/sims/full_steady_heat.h5 index b3a850bcff..e591c4b259 100644 Binary files a/tests/sims/full_steady_heat.h5 and b/tests/sims/full_steady_heat.h5 differ diff --git a/tests/sims/full_steady_heat.json b/tests/sims/full_steady_heat.json index bdf682f1ac..86c240fac6 100644 --- a/tests/sims/full_steady_heat.json +++ b/tests/sims/full_steady_heat.json @@ -273,7 +273,7 @@ "min_edges_per_side": 2.0, "non_refined_structures": [] }, - "version": "2.9.0", + "version": "2.9.1", "plot_length_units": "\u03bcm", "structure_priority_mode": "equal", "analysis_spec": null diff --git a/tests/sims/full_unsteady_heat.h5 b/tests/sims/full_unsteady_heat.h5 index 3420ecf4f2..0dd8d18892 100644 Binary files a/tests/sims/full_unsteady_heat.h5 and b/tests/sims/full_unsteady_heat.h5 differ diff --git a/tests/sims/full_unsteady_heat.json b/tests/sims/full_unsteady_heat.json index 7c91c47410..3794ca1dcc 100644 --- a/tests/sims/full_unsteady_heat.json +++ b/tests/sims/full_unsteady_heat.json @@ -273,7 +273,7 @@ "min_edges_per_side": 2.0, "non_refined_structures": [] }, - "version": "2.9.0", + "version": "2.9.1", "plot_length_units": "\u03bcm", "structure_priority_mode": "equal", "analysis_spec": { diff --git a/tests/test_components/test_autograd_mode_polyslab_numerical.py b/tests/test_components/test_autograd_mode_polyslab_numerical.py index d86af1c3b7..bcb8127c67 100644 --- a/tests/test_components/test_autograd_mode_polyslab_numerical.py +++ b/tests/test_components/test_autograd_mode_polyslab_numerical.py @@ -457,7 +457,7 @@ def eval_fn(sim_data): fd_mag = np.linalg.norm(fd_grad) adj_mag = np.linalg.norm(pattern_dot_adj_gradient) percentage_error = 100.0 * np.mean( - (fd_grad - pattern_dot_adj_gradient) / (fd_grad + np.finfo(np.float64).eps) + np.abs(fd_grad - pattern_dot_adj_gradient) / (np.abs(fd_grad) + np.finfo(np.float64).eps) ) print("\n" * 3) diff --git a/tests/test_components/test_autograd_numerical.py b/tests/test_components/test_autograd_numerical.py index 6def75ec10..836bf9edde 100644 --- a/tests/test_components/test_autograd_numerical.py +++ b/tests/test_components/test_autograd_numerical.py @@ -333,7 +333,7 @@ def test_finite_difference_field_data(field_data_test_parameters, rng, tmp_path, fd_mag = np.linalg.norm(fd_grad) adj_mag = np.linalg.norm(pattern_dot_adj_gradient) percentage_error = 100.0 * np.mean( - (fd_grad - pattern_dot_adj_gradient) / (fd_grad + np.finfo(np.float64).eps) + np.abs(fd_grad - pattern_dot_adj_gradient) / (np.abs(fd_grad) + np.finfo(np.float64).eps) ) print("\n" * 3) diff --git a/tests/test_components/test_autograd_periodic_numerical.py b/tests/test_components/test_autograd_periodic_numerical.py new file mode 100644 index 0000000000..a71a34292a --- /dev/null +++ b/tests/test_components/test_autograd_periodic_numerical.py @@ -0,0 +1,430 @@ +# test autograd and compares to numerically computed finite difference gradients +from __future__ import annotations + +import operator +import sys + +import autograd as ag +import matplotlib.pylab as plt +import numpy as np +import pytest +from scipy.ndimage import gaussian_filter + +import tidy3d as td +import tidy3d.web as web + +PLOT_FD_ADJ_COMPARISON = False +NUM_FINITE_DIFFERENCE = 10 +SAVE_FD_ADJ_DATA = True +SAVE_FD_LOC = 0 +SAVE_ADJ_LOC = 1 +LOCAL_GRADIENT = False +VERBOSE = False +NUMERICAL_RESULTS_DATA_DIR = "./numerical_periodic_test/" +SHOW_PRINT_STATEMENTS = False + +RMS_THRESHOLD = 0.25 + +if PLOT_FD_ADJ_COMPARISON: + pytestmark = pytest.mark.usefixtures("mpl_config_interactive") +else: + pytestmark = pytest.mark.usefixtures("mpl_config_noninteractive") + +if SHOW_PRINT_STATEMENTS: + sys.stdout = sys.stderr + + +FINITE_DIFF_PERM_SEED = 1.5**2 +MESH_FACTOR_DESIGN = 30.0 + + +def get_sim_geometry(mesh_wvl_um): + return td.Box( + size=(3.5 * mesh_wvl_um, 3.5 * mesh_wvl_um, 7 * mesh_wvl_um), + center=(3.5 * mesh_wvl_um / 4.0, 0, 0), + ) + + +def make_base_sim( + mesh_wvl_um, + adj_wvl_um, + box_for_override, + pw_angle_deg, + grating_mode, + monitor_bg_index=1.0, + run_time=1e-11, +): + sim_geometry = get_sim_geometry(mesh_wvl_um) + sim_size_um = sim_geometry.size + sim_center_um = sim_geometry.center + + dl_design = mesh_wvl_um / MESH_FACTOR_DESIGN + + mesh_overrides = [] + mesh_overrides.extend( + [ + td.MeshOverrideStructure( + geometry=box_for_override, + dl=[dl_design, dl_design, dl_design], + ), + ] + ) + + src_size = sim_size_um[0:2] + (0,) + + wl_min_src_um = 0.9 * adj_wvl_um + wl_max_src_um = 1.1 * adj_wvl_um + + fwidth_src = td.C_0 * ((1.0 / wl_min_src_um) - (1.0 / wl_max_src_um)) + freq0 = td.C_0 / adj_wvl_um + + pulse = td.GaussianPulse(freq0=freq0, fwidth=fwidth_src) + + src = td.PlaneWave( + center=(1.0, 0, -0.25 * sim_size_um[2]), + size=[td.inf, td.inf, 0], + source_time=pulse, + direction="+", + angle_theta=(pw_angle_deg * np.pi / 180.0), + ) + + bloch_x = td.Boundary.bloch_from_source( + source=src, + domain_size=sim_size_um[0], + axis=0, + ) + bloch_y = td.Boundary.bloch_from_source( + source=src, + domain_size=sim_size_um[1], + axis=1, + ) + + boundary_spec = td.BoundarySpec( + x=bloch_x, + y=bloch_y, + z=td.Boundary.pml(num_layers=48), + ) + + assert (grating_mode == "transmission") or (grating_mode == "reflection"), ( + "Unknown grating mode specified!" + ) + if grating_mode == "transmission": + diffraction_monitor = td.DiffractionMonitor( + center=( + 0, + sim_center_um[1], + 0.25 * sim_size_um[2], + ), + size=(np.inf, np.inf, 0), + name="monitor_diffraction", + freqs=[freq0], + normal_dir="+", + ) + else: + diffraction_monitor = td.DiffractionMonitor( + # center=(0, 0, -0.35 * sim_size_um[2]), + center=(sim_center_um[0], sim_center_um[1], -0.35 * sim_size_um[2]), + size=(np.inf, np.inf, 0), + name="monitor_diffraction", + freqs=[freq0], + normal_dir="-", + ) + + monitor_index_block = td.Box( + center=(sim_center_um[0], sim_center_um[1], 0.25 * sim_size_um[2] + mesh_wvl_um), + size=(*tuple(2 * size for size in sim_size_um[0:2]), mesh_wvl_um + 0.5 * sim_size_um[2]), + ) + monitor_index_block_structure = td.Structure( + geometry=monitor_index_block, medium=td.Medium(permittivity=monitor_bg_index**2) + ) + + sim_base = td.Simulation( + center=sim_center_um, + size=sim_size_um, + grid_spec=td.GridSpec.auto( + min_steps_per_wvl=30, + wavelength=mesh_wvl_um, + override_structures=mesh_overrides, + ), + structures=[monitor_index_block_structure], + sources=[src], + monitors=[diffraction_monitor], + run_time=run_time, + boundary_spec=boundary_spec, + subpixel=True, + ) + + return sim_base + + +def create_objective_function(geometry, create_sim_base, eval_fn, sim_path_dir): + def objective(perm_arrays): + sim_base = create_sim_base() + + simulation_dict = {} + for idx in range(len(perm_arrays)): + block_structure = td.Structure.from_permittivity_array( + eps_data=perm_arrays[idx], + geometry=geometry, + ) + + sim_with_block = sim_base.updated_copy( + structures=(*sim_base.structures, block_structure) + ) + + simulation_dict[f"numerical_periodic_testing_{idx}"] = sim_with_block.copy() + + sim_data = web.run_async( + simulation_dict, path_dir=sim_path_dir, local_gradient=LOCAL_GRADIENT, verbose=VERBOSE + ) + + objective_vals = [] + for idx in range(len(perm_arrays)): + objective_vals.append(eval_fn(sim_data[f"numerical_periodic_testing_{idx}"])) + + if len(perm_arrays) == 1: + return objective_vals[0] + + return objective_vals + + return objective + + +def make_eval_fns(orders_x, orders_y, polarization): + def transmission_order_pol_amp_sq(sim_data): + total = 0.0 + + for order_x_val in orders_x: + for order_y_val in orders_y: + total += np.sum( + np.abs( + sim_data["monitor_diffraction"] + .amps.sel( + polarization=polarization, orders_x=order_x_val, orders_y=order_y_val + ) + .data + ) + ** 2 + ) + + return total + + eval_fns = [transmission_order_pol_amp_sq] + eval_fn_names = [f"transmission_order_pol_amp_sq_{orders_x}_{orders_y}_{polarization}"] + + return eval_fns, eval_fn_names + + +background_indices = [1.0, 1.5] +mesh_wvls_um = [1.55] +adj_wvls_um = [1.55] + +orders_x = [(0,), (1,), (2,), (1, 2)] +orders_y = [(0,), (0,), (0,), (1,)] +polarizations = ["p", "p", "p", "s"] + +grating_modes = ["transmission", "reflection"] + +pw_angles_deg = [0.0, 10.0] + +periodic_test_parameters = [] + +test_number = 0 +for idx in range(len(mesh_wvls_um)): + mesh_wvl_um = mesh_wvls_um[idx] + adj_wvl_um = adj_wvls_um[idx] + + for grating_mode in grating_modes: + for order_idx in range(len(orders_x)): + eval_fns, eval_fn_names = make_eval_fns( + orders_x=orders_x[order_idx], + orders_y=orders_y[order_idx], + polarization=polarizations[order_idx], + ) + + for pw_angle_deg in pw_angles_deg: + for monitor_bg_index in background_indices: + for eval_fn_idx, eval_fn in enumerate(eval_fns): + periodic_test_parameters.append( + { + "mesh_wvl_um": mesh_wvl_um, + "adj_wvl_um": adj_wvl_um, + "monitor_bg_index": monitor_bg_index, + "pw_angle_deg": pw_angle_deg, + "order_x": orders_x[order_idx], + "order_y": orders_y[order_idx], + "polarization": polarizations[order_idx], + "grating_mode": grating_mode, + "eval_fn": eval_fn, + "eval_fn_name": eval_fn_names[eval_fn_idx], + "test_number": test_number, + } + ) + + test_number += 1 + + +@pytest.mark.numerical +@pytest.mark.parametrize( + "periodic_test_parameters, dir_name", + zip( + periodic_test_parameters, + ([NUMERICAL_RESULTS_DATA_DIR] if SAVE_FD_ADJ_DATA else [None]) + * len(periodic_test_parameters), + ), + indirect=["dir_name"], +) +def test_finite_difference_diffraction_data( + periodic_test_parameters, rng, tmp_path, create_directory +): + """Test a variety of autograd permittivity gradients for DiffractionData by""" + """comparing them to numerical finite difference.""" + + test_results = np.zeros((2, NUM_FINITE_DIFFERENCE)) + + test_number = periodic_test_parameters["test_number"] + + ( + mesh_wvl_um, + adj_wvl_um, + monitor_bg_index, + pw_angle_deg, + order_x, + order_y, + polarization, + grating_mode, + eval_fn, + eval_fn_name, + test_number, + ) = operator.itemgetter( + "mesh_wvl_um", + "adj_wvl_um", + "monitor_bg_index", + "pw_angle_deg", + "order_x", + "order_y", + "polarization", + "grating_mode", + "eval_fn", + "eval_fn_name", + "test_number", + )(periodic_test_parameters) + + sim_geometry = get_sim_geometry(mesh_wvl_um) + + dim_um = mesh_wvl_um + thickness_um = 0.5 * mesh_wvl_um + block = td.Box( + center=(sim_geometry.center[0], sim_geometry.center[1], 0), + size=(dim_um, dim_um, thickness_um), + ) + + dim = 1 + int(dim_um / (mesh_wvl_um / MESH_FACTOR_DESIGN)) + Nz = 1 + int(thickness_um / (mesh_wvl_um / MESH_FACTOR_DESIGN)) + + box_for_override = td.Box( + center=(sim_geometry.center[0], sim_geometry.center[1], 0), + size=sim_geometry.size[0:2] + (thickness_um + mesh_wvl_um,), + ) + + eval_fns, eval_fn_names = make_eval_fns( + orders_x=order_x, orders_y=order_y, polarization=polarization + ) + + sim_path_dir = tmp_path / f"test{test_number}" + sim_path_dir.mkdir() + + objective = create_objective_function( + block, + lambda mesh_wvl_um=mesh_wvl_um, + adj_wvl_um=adj_wvl_um, + box_for_override=box_for_override, + pw_angle_deg=pw_angle_deg, + grating_mode=grating_mode, + monitor_bg_index=monitor_bg_index: make_base_sim( + mesh_wvl_um=mesh_wvl_um, + adj_wvl_um=adj_wvl_um, + box_for_override=box_for_override, + pw_angle_deg=pw_angle_deg, + grating_mode=grating_mode, + monitor_bg_index=monitor_bg_index, + ), + eval_fn, + sim_path_dir=str(sim_path_dir), + ) + + obj_val_and_grad = ag.value_and_grad(objective) + + perm_init = FINITE_DIFF_PERM_SEED * np.ones((dim, dim, Nz)) + + obj, adj_grad = obj_val_and_grad([perm_init]) + + # empirical step size from running other finite difference tests for field + # cases with permittivity + fd_step = 0.1 + + all_perm = [] + pattern_dot_adj_gradient = np.zeros(NUM_FINITE_DIFFERENCE) + + for fd_idx in range(NUM_FINITE_DIFFERENCE): + random_pattern = rng.random((dim, dim, Nz)) - 0.5 + random_pattern = gaussian_filter(random_pattern, sigma=3) + random_pattern /= np.linalg.norm(random_pattern) + + pattern_dot_adj_gradient[fd_idx] = np.sum(random_pattern * adj_grad) + + perm_up = perm_init.copy() + fd_step * random_pattern + perm_down = perm_init.copy() - fd_step * random_pattern + + all_perm.append(perm_up) + all_perm.append(perm_down) + + all_obj = objective(all_perm) + + fd_grad = np.zeros(NUM_FINITE_DIFFERENCE) + for fd_idx in range(NUM_FINITE_DIFFERENCE): + obj_up_location = 2 * fd_idx + obj_down_location = 2 * fd_idx + 1 + + fd_grad[fd_idx] = (all_obj[obj_up_location] - all_obj[obj_down_location]) / (2 * fd_step) + + rms_error = np.linalg.norm(fd_grad - pattern_dot_adj_gradient) + fd_mag = np.linalg.norm(fd_grad) + adj_mag = np.linalg.norm(pattern_dot_adj_gradient) + + percentage_error = 100.0 * np.mean( + np.abs(fd_grad - pattern_dot_adj_gradient) / (np.abs(fd_grad) + np.finfo(np.float64).eps) + ) + + print("\n" * 3) + print("-" * 20) + print(f"Numerical test #{test_number}") + print(f"Mesh and adjoint wavelengths: {mesh_wvl_um}, {adj_wvl_um}") + print(f"Input plane wave angle (deg): {pw_angle_deg}") + print(f"(X, Y) order, polarization: ({order_x}, {order_y}), {polarization}") + print(f"Background index for monitor: {monitor_bg_index}") + print(f"Eval function: {eval_fn_name}") + print(f"RMS Error: {rms_error}") + print(f"FD, Adj magnitudes: {fd_mag}, {adj_mag}") + print(f"Percentage Error: {percentage_error}") + print("-" * 20) + print("\n" * 3) + + assert rms_error < RMS_THRESHOLD * fd_mag, "RMS error magnitude too large" + + test_results[SAVE_FD_LOC, :] = fd_grad + test_results[SAVE_ADJ_LOC, :] = pattern_dot_adj_gradient + + test_number += 1 + + if PLOT_FD_ADJ_COMPARISON: + plt.plot(pattern_dot_adj_gradient, color="g", linewidth=2.0) + plt.plot(fd_grad, color="b", linewidth=1.5, linestyle="--") + plt.title(f"Gradient for objective: {eval_fn_name}") + plt.legend(["Finite difference", "Adjoint"]) + plt.xlabel("Sample number") + plt.ylabel("Gradient value") + plt.show() + + if SAVE_FD_ADJ_DATA: + np.save(f"{NUMERICAL_RESULTS_DATA_DIR}/results_{test_number}.npy", test_results) diff --git a/tests/test_components/test_geometry.py b/tests/test_components/test_geometry.py index 6fb6f6ae3d..b0cba6f2bd 100644 --- a/tests/test_components/test_geometry.py +++ b/tests/test_components/test_geometry.py @@ -1286,8 +1286,7 @@ def test_cleanup_shapely_object(): # Test using a non-empty exterior polygon (big_square_5x5) orig_polygon = shapely.Polygon(exterior_coords, interior_coords_list) new_polygon = cleanup_shapely_object(orig_polygon, tolerance_ratio=1e-12) - # Delete any nearby or overlapping vertices (cleanup_shapely_object() does not do this). - new_polygon = shapely.simplify(new_polygon, tolerance=1e-10) + # Delete any nearby or overlapping vertices (cleanup_shapely_object() now does this). # Now `new_polygon` should only contain the coordinates of the square (with a duplicate at end). assert len(new_polygon.exterior.coords) == 5 # squares have 4 vertices but shapely adds 1 assert len(new_polygon.interiors) == 1 # only the "triangle_empty_tails" interior hole survives @@ -1296,5 +1295,4 @@ def test_cleanup_shapely_object(): exterior_coords = triangle_collinear # has zero area orig_polygon = shapely.Polygon(exterior_coords) new_polygon = cleanup_shapely_object(orig_polygon, tolerance_ratio=1e-12) - new_polygon = shapely.simplify(new_polygon, tolerance=1e-10) assert len(new_polygon.exterior.coords) == 0 # empty / collinear polygons should get deleted diff --git a/tests/test_components/test_layerrefinement.py b/tests/test_components/test_layerrefinement.py index 506234ee01..5a275753df 100644 --- a/tests/test_components/test_layerrefinement.py +++ b/tests/test_components/test_layerrefinement.py @@ -801,3 +801,72 @@ def test_gap_meshing(): # sim.plot(x=0, ax=ax) # sim.plot_grid(x=0, ax=ax) # plt.show() + + +def test_gap_meshing_skip_small_gap(): + """When the gap is very small, make sure it's skipped.""" + + f0 = 7e9 + + mm = 1000 # Conversion mm to micron + H = 0.8 * mm # Substrate thickness + T = 0.035 * mm # Metal thickness + + # Resonator dimensions + MA, MB, MC, MD = (3.9 * mm, 7.1 * mm, 3.1 * mm, 2.3 * mm) + ME, MF, MG, MH = (0.6 * mm, 0.2 * mm, 1.2 * mm, 0.5 * mm) + MJ, MK, MM, MN = (4.8 * mm, 0.3 * mm, 0.1 * mm, 0.7 * mm) + MP, MQ, MR, MS = (0.1 * mm, 0.7 * mm, 0.4 * mm, 0.3 * mm) + Lsub, Wsub = (2 * MC + MH, 2 * (MH + MK + MB)) + + geom_patch = td.Box.from_bounds( + rmin=(-MA / 2, MH / 2 + MK, 0), rmax=(MA / 2, MH / 2 + MK + MB, T) + ) + geom_hole1 = td.Box.from_bounds( + rmin=(-MH / 2 - MN - MF - ME, MH / 2 + MK + MS, 0), + rmax=(-MH / 2 - MN - MF, MH / 2 + MK + MS + MG, T), + ) + geom_hole5 = td.Box.from_bounds( + rmin=(-MA / 2 + 1.5 * MF, MH / 2 + MK + MS + MG + MQ, 0), + rmax=(-MA / 2 + 1.5 * MF + MM, MH / 2 + MK + MB - MP, T), + ) + geom_hole6 = geom_hole5.translated(-2 * geom_hole5.center[0], 0, 0) + geom_hole7 = td.Box.from_bounds( + rmin=(-MH / 2 - MN, MH / 2 + MK, 0), rmax=(MH / 2 + MN, MH / 2 + MD, T) + ) + for hole in [geom_hole1, geom_hole5, geom_hole6, geom_hole7]: + geom_patch -= hole + + x0, y0, z0 = geom_patch.bounding_box.center + struct_patch = td.Structure(geometry=geom_patch, medium=td.PEC) + + # Add padding + padding = td.C_0 / f0 / 2 + sim_LX = Lsub + padding + sim_LY = Wsub + padding + sim_LZ = H + padding + + # Layer refinement on resonator + lr_spec = td.LayerRefinementSpec.from_structures( + structures=[struct_patch], + min_steps_along_axis=1, + corner_refinement=td.GridRefinement(dl=T, num_cells=2), + dl_min_from_gap_width=True, + ) + + # Define overall grid spec + grid_spec = td.GridSpec.auto( + wavelength=td.C_0 / f0, + min_steps_per_wvl=12, + layer_refinement_specs=[lr_spec], + ) + + # Define simulation object + sim = td.Simulation( + center=(x0, y0, z0), + size=(sim_LX, sim_LY, sim_LZ), + structures=[struct_patch], + grid_spec=grid_spec, + run_time=1e-9, + ) + assert sim.grid_info["min_grid_size"] > 20 diff --git a/tests/test_data/test_monitor_data.py b/tests/test_data/test_monitor_data.py index c0f70d6454..7acb0b6516 100644 --- a/tests/test_data/test_monitor_data.py +++ b/tests/test_data/test_monitor_data.py @@ -906,6 +906,17 @@ def field_data(self) -> td.FieldData: ) return self.simdata(monitor)["fields"] + @pytest.fixture(scope="class") + def field_data_single_frequency(self) -> td.FieldData: + """Make random field data with single frequency from an emulated simulation run.""" + monitor = td.FieldMonitor( + size=(td.inf, td.inf, 0), + freqs=self.freqs[0], + name="fields", + colocate=True, + ) + return self.simdata(monitor)["fields"] + @pytest.fixture(scope="class") def mode_data(self) -> td.ModeData: """Make random ModeData from an emulated simulation run.""" @@ -919,18 +930,41 @@ def mode_data(self) -> td.ModeData: ) return self.simdata(monitor)["modes"] + @pytest.fixture(scope="class") + def mode_data_single_frequency(self) -> td.ModeData: + """Make random ModeData from an emulated simulation run.""" + monitor = td.ModeMonitor( + size=(td.inf, td.inf, 0), + freqs=self.freqs[0], + name="modes", + colocate=True, + mode_spec=td.ModeSpec(num_modes=2, target_neff=4.0), + store_fields_direction="+", + ) + return self.simdata(monitor)["modes"] + + @pytest.mark.parametrize("field_data_fixture", ["field_data", "field_data_single_frequency"]) @pytest.mark.parametrize("background_index", [1, 2, 3]) @pytest.mark.parametrize("freq", [*list(freqs), None]) @pytest.mark.parametrize("n_x", [2**5, 2**6]) @pytest.mark.parametrize("n_y", [2**5, 2**6]) @pytest.mark.parametrize("units", ["mm", "cm", "in", "m"]) def test_fielddata_tozbf_readzbf( - self, tmp_path, field_data, background_index, freq, n_x, n_y, units + self, + tmp_path, + request, + field_data_fixture, + background_index, + freq, + n_x, + n_y, + units, ): """Test that FieldData.to_zbf() -> ZBFData.read_zbf() works""" zbf_filename = tmp_path / "testzbf.zbf" # write to zbf and then load it back in + field_data = request.getfixturevalue(field_data_fixture) ex, ey = field_data.to_zbf( fname=zbf_filename, background_refractive_index=background_index, @@ -960,13 +994,20 @@ def test_fielddata_tozbf_readzbf( assert np.allclose(ex.values, zbfdata.Ex) assert np.allclose(ey.values, zbfdata.Ey) + @pytest.mark.parametrize("mode_data_fixture", ["mode_data", "mode_data_single_frequency"]) @pytest.mark.parametrize("mode_index", [0, 1]) - def test_tozbf_modedata(self, tmp_path, mode_data, mode_index): + def test_tozbf_modedata( + self, + tmp_path, + request, + mode_data_fixture, + mode_index, + ): """Tests ModeData.to_zbf()""" zbf_filename = tmp_path / "testzbf_modedata.zbf" # write to zbf and then load it back in - ex, ey = mode_data.to_zbf( + ex, ey = request.getfixturevalue(mode_data_fixture).to_zbf( fname=zbf_filename, background_refractive_index=1, freq=self.freq0, diff --git a/tests/test_plugins/smatrix/test_terminal_component_modeler.py b/tests/test_plugins/smatrix/test_terminal_component_modeler.py index c601d34e76..8d92c6b8df 100644 --- a/tests/test_plugins/smatrix/test_terminal_component_modeler.py +++ b/tests/test_plugins/smatrix/test_terminal_component_modeler.py @@ -386,6 +386,22 @@ def test_coaxial_port_snapping(tmp_path): check_lumped_port_components_snapped_correctly(modeler=modeler) +@pytest.mark.parametrize("axis", [0, 1, 2]) +def test_coaxial_port_source_size(axis): + """Make sure source size is correct.""" + port = CoaxialLumpedPort( + center=(0, 0, 0), + inner_diameter=1, + outer_diameter=2, + normal_axis=axis, + direction="+", + name="port", + impedance=50, + ) + source = port.to_source(td.GaussianPulse(freq0=1e10, fwidth=1e9)) + assert np.isclose(source.size[axis], 0) + + def test_power_delivered_helper(monkeypatch, tmp_path): """Test computations involving power waves are correct by manually setting voltage and current at ports using monkeypatch. diff --git a/tests/test_plugins/test_mode_solver.py b/tests/test_plugins/test_mode_solver.py index 9e52ca06ea..dc7de4d31c 100644 --- a/tests/test_plugins/test_mode_solver.py +++ b/tests/test_plugins/test_mode_solver.py @@ -286,6 +286,26 @@ def test_mode_solver_validation(): direction="+", ) + # num of modes * plane grid points too large + # 1) number of modes too big + with pytest.raises(SetupError): + ms = ModeSolver( + simulation=simulation, + plane=PLANE, + mode_spec=mode_spec.updated_copy(num_modes=2**32), + freqs=[1e12], + direction="+", + ) + # 2) number of grid points too big + with pytest.raises(SetupError): + ms = ModeSolver( + simulation=simulation.updated_copy(grid_spec=td.GridSpec.uniform(dl=0.0001)), + plane=PLANE, + mode_spec=mode_spec, + freqs=[1e12], + direction="+", + ) + # mode data too large simulation = td.Simulation( size=SIM_SIZE, diff --git a/tests/test_web/test_webapi.py b/tests/test_web/test_webapi.py index 2f63da9d20..60d06b9311 100644 --- a/tests/test_web/test_webapi.py +++ b/tests/test_web/test_webapi.py @@ -1,6 +1,8 @@ # Tests webapi and things that depend on it from __future__ import annotations +import os + import numpy as np import pytest import responses @@ -656,6 +658,40 @@ def test_create_output_dirs(mock_webapi, tmp_path, monkeypatch): assert non_existent_dirs_batch.is_dir() +@responses.activate +def test_batch_run_saves_file_after_upload(mock_webapi, mock_job_status, tmp_path, monkeypatch): + """Test that batch.run() saves batch file with task_ids immediately after upload.""" + sims = {TASK_NAME: make_sim()} + batch = Batch(simulations=sims, folder_name=PROJECT_NAME) + + batch_file_saved = {"saved": False, "has_task_ids": False} + original_to_file = Batch.to_file + + def track_to_file(self, fname): + batch_file_saved["saved"] = True + batch_file_saved["has_task_ids"] = self.jobs is not None and TASK_NAME in self.jobs + return original_to_file(self, fname) + + # mock start to interrupt run() after upload and to_file + def mock_start_interrupt(self): + # at this point, upload() and to_file() should have been called + assert batch_file_saved["saved"], "Batch file should be saved before start()" + assert batch_file_saved["has_task_ids"], "Batch file should have task_ids" + # verify file actually exists and can be loaded + batch_path = self._batch_path(path_dir=str(tmp_path)) + assert os.path.exists(batch_path) + recovered = Batch.from_file(batch_path) + assert recovered.jobs[TASK_NAME].task_id == TASK_ID + raise RuntimeError("Simulated interruption after upload") + + monkeypatch.setattr(Batch, "to_file", track_to_file) + monkeypatch.setattr(Batch, "start", mock_start_interrupt) + + # run should save the batch file after upload, even if interrupted + with pytest.raises(RuntimeError, match="Simulated interruption"): + batch.run(path_dir=str(tmp_path)) + + """ Async """ diff --git a/tidy3d/__init__.py b/tidy3d/__init__.py index a3993e3863..d1d9a10d5e 100644 --- a/tidy3d/__init__.py +++ b/tidy3d/__init__.py @@ -208,7 +208,7 @@ from .components.field_projection import FieldProjector # frequency conversion utilities -from .components.frequencies import FreqRange, frequencies, wavelengths +from .components.frequencies import FreqRange, FrequencyUtils, frequencies, wavelengths # geometry from .components.geometry.base import Box, ClipOperation, Geometry, GeometryGroup, Transformed @@ -544,6 +544,7 @@ def set_logging_level(level: str) -> None: "FluxTimeMonitor", "FossumCarrierLifetime", "FreqRange", + "FrequencyUtils", "FullyAnisotropicMedium", "GaussianBeam", "GaussianBeamProfile", diff --git a/tidy3d/components/data/monitor_data.py b/tidy3d/components/data/monitor_data.py index a9dfea71d3..638b70b682 100644 --- a/tidy3d/components/data/monitor_data.py +++ b/tidy3d/components/data/monitor_data.py @@ -1163,9 +1163,14 @@ def to_zbf( else: freq = freq.item() - mode_area = mode_area.interp(f=freq) - e_x = e_x.interp(f=freq) - e_y = e_y.interp(f=freq) + # If the data has just one frequency, avoid Nans at the interpolation + if len(e_x.f) > 1: + mode_area = mode_area.interp(f=freq) + e_x = e_x.interp(f=freq) + e_y = e_y.interp(f=freq) + else: + e_x = e_x.isel(f=0, drop=True) + e_y = e_y.isel(f=0, drop=True) # If the data is ModeData, choose one of the modes to save if "mode_index" in e_x.coords: @@ -1241,7 +1246,7 @@ def to_zbf( ) fout.write(struct.pack("<8d", 0, 0, 0, 0, 0, 0, 0, 0)) # unused values for e in (e_x, e_y): - e_flat = e.values.flatten(order="C") + e_flat = e.values.flatten(order="F") # Interweave real and imaginary parts e_values = np.ravel(np.column_stack((e_flat.real, e_flat.imag))) fout.write(struct.pack(f"<{2 * n_x * n_y}d", *e_values)) @@ -3448,7 +3453,13 @@ def adjoint_source_amp(self, amp: DataArray, fwidth: float) -> PlaneWave: k0 = 2 * np.pi * freq0 / C_0 bck_eps = self.medium.eps_model(freq0) grad_const = 0.5 * k0 / np.sqrt(bck_eps) * np.cos(angle_theta) - src_amp = 1j * grad_const * amp_complex + + normal_factor = 1.0 if (self.monitor.normal_dir == "+") else -1.0 + src_amp = 1j * grad_const * amp_complex * normal_factor + # the angular direction for sources and monitors when the normal is "-" + # differs by a sign, so we need to flip the angle here when the normal + # is "-" + src_angle_theta = normal_factor * angle_theta # construct plane wave source adj_src = PlaneWave( @@ -3461,7 +3472,7 @@ def adjoint_source_amp(self, amp: DataArray, fwidth: float) -> PlaneWave: fwidth=fwidth, ), direction=self.flip_direction(monitor.normal_dir), - angle_theta=angle_theta, + angle_theta=src_angle_theta, angle_phi=angle_phi, pol_angle=pol_angle, ) diff --git a/tidy3d/components/data/sim_data.py b/tidy3d/components/data/sim_data.py index d4deadeed8..c4567d8033 100644 --- a/tidy3d/components/data/sim_data.py +++ b/tidy3d/components/data/sim_data.py @@ -1192,7 +1192,7 @@ def _process_adjoint_sources(self, adj_srcs: list[SourceType]) -> list[AdjointSo # warn if the forward simulation had symmetry and we are grouping by port, which # which means the individual adjoint simulations may not respect the original symmetry # - if np.any(np.abs(self.simulation.symmetry) > 0): + if np.any(np.abs(self.simulation.symmetry) > 0) and (num_ports > 1): log.warning( "The adjoint simulations for this problem are being broken into " "multiple simulations that may not individually respect the symmetry of the " diff --git a/tidy3d/components/data/zbf.py b/tidy3d/components/data/zbf.py index d05d058d25..08d9870216 100644 --- a/tidy3d/components/data/zbf.py +++ b/tidy3d/components/data/zbf.py @@ -121,11 +121,11 @@ def read_zbf(filename: str) -> ZBFData: ) from None # load E field - Ex_real = np.asarray(rawx[0::2]).reshape(nx, ny) - Ex_imag = np.asarray(rawx[1::2]).reshape(nx, ny) + Ex_real = np.asarray(rawx[0::2]).reshape(nx, ny, order="F") + Ex_imag = np.asarray(rawx[1::2]).reshape(nx, ny, order="F") if ispol: - Ey_real = np.asarray(rawy[0::2]).reshape(nx, ny) - Ey_imag = np.asarray(rawy[1::2]).reshape(nx, ny) + Ey_real = np.asarray(rawy[0::2]).reshape(nx, ny, order="F") + Ey_imag = np.asarray(rawy[1::2]).reshape(nx, ny, order="F") else: Ey_real = np.zeros((nx, ny)) Ey_imag = np.zeros((nx, ny)) diff --git a/tidy3d/components/frequencies.py b/tidy3d/components/frequencies.py index 2fbffc090b..390ffac48b 100644 --- a/tidy3d/components/frequencies.py +++ b/tidy3d/components/frequencies.py @@ -20,7 +20,7 @@ class FrequencyUtils(Tidy3dBaseModel): - """Class for general frequency/wavelength utilities.""" + """Utilities for classifying frequencies/wavelengths and generating samples for standard optical bands.""" use_wavelength: bool = pd.Field( False, @@ -235,6 +235,16 @@ def u_band(self, n: int = 11) -> list[float]: frequencies = FrequencyUtils(use_wavelength=False) wavelengths = FrequencyUtils(use_wavelength=True) +frequencies.__doc__ = ( + "Frequency utilities configured to interpret and return values in hertz (Hz). " + "Use for RF, microwave, optical, and other band classifications in frequency units." +) + +wavelengths.__doc__ = ( + "Frequency utilities configured to interpret and return values in micrometers (μm). " + "Use for optical and photonic calculations where wavelength units are preferred." +) + class FreqRange(Tidy3dBaseModel): """ diff --git a/tidy3d/components/geometry/base.py b/tidy3d/components/geometry/base.py index de00fde6f9..b00440b5e7 100644 --- a/tidy3d/components/geometry/base.py +++ b/tidy3d/components/geometry/base.py @@ -66,6 +66,7 @@ POLY_GRID_SIZE = 1e-12 POLY_TOLERANCE_RATIO = 1e-12 +POLY_DISTANCE_TOLERANCE = 8e-12 _shapely_operations = { @@ -3397,6 +3398,9 @@ def cleanup_shapely_object(obj: Shapely, tolerance_ratio: float = POLY_TOLERANCE cap_style="square", quad_segs=3, ) + # Clean vertices of very close distances created during the erosion/dilation process. + # The distance value is heuristic. + cleaned_obj = cleaned_obj.simplify(POLY_DISTANCE_TOLERANCE, preserve_topology=True) # Revert to the original scale and position. rescaled_clean_obj = shapely.affinity.affine_transform( cleaned_obj, diff --git a/tidy3d/components/grid/grid_spec.py b/tidy3d/components/grid/grid_spec.py index 10b4d0e38d..1fdbc419e9 100644 --- a/tidy3d/components/grid/grid_spec.py +++ b/tidy3d/components/grid/grid_spec.py @@ -10,6 +10,7 @@ from tidy3d.components.base import Tidy3dBaseModel, cached_property, skip_if_fields_missing from tidy3d.components.geometry.base import Box, ClipOperation +from tidy3d.components.geometry.utils_2d import increment_float from tidy3d.components.lumped_element import LumpedElementType from tidy3d.components.source.utils import SourceType from tidy3d.components.structure import MeshOverrideStructure, Structure, StructureType @@ -1323,6 +1324,13 @@ def _is_inplane_bounded(self) -> bool: self.size[(self.axis + 2) % 3] ) + @cached_property + def _slightly_enlarged_box(self) -> Box: + """Slightly enlarged box for robust point containment querying.""" + # increase size slightly + size = [increment_float(orig_length, 1) for orig_length in self.size] + return Box(center=self.center, size=size) + def _unpop_axis(self, ax_coord: float, plane_coord: Any) -> CoordinateOptional: """Combine coordinate along axis with identical coordinates on the plane tangential to the axis. @@ -1411,7 +1419,7 @@ def _inplane_inside(self, point: ArrayFloat2D) -> bool: point_3d = self.unpop_axis( ax_coord=self.center[self.axis], plane_coords=point, axis=self.axis ) - return self.inside(point_3d[0], point_3d[1], point_3d[2]) + return self._slightly_enlarged_box.inside(point_3d[0], point_3d[1], point_3d[2]) def _corners_and_convexity_2d( self, structure_list: list[Structure], ravel: bool @@ -1622,6 +1630,14 @@ def _find_vertical_intersections( if ind_end == ind_beg: continue + # intersects one grid line but almost parallel to it + if np.abs(ind_end - ind_beg) == 1 and np.abs( + v_beg[0] - v_end[0] + ) < 2 * GAP_MESHING_TOL * np.abs( + grid_x_coords[ind_beg - 1] - grid_x_coords[ind_end - 1] + ): + continue + # sort vertices in ascending order to make treatmeant unifrom reverse = False if ind_beg > ind_end: diff --git a/tidy3d/components/mode/mode_solver.py b/tidy3d/components/mode/mode_solver.py index 6dcc89e27d..c6cc749f15 100644 --- a/tidy3d/components/mode/mode_solver.py +++ b/tidy3d/components/mode/mode_solver.py @@ -243,6 +243,7 @@ def _post_init_validators(self) -> None: ) self._warn_thick_pml(simulation=self.simulation, plane=self.plane, mode_spec=self.mode_spec) self._validate_rotate_structures() + self._validate_num_grid_points() @classmethod def _warn_thick_pml( @@ -303,6 +304,18 @@ def _validate_rotate_structures(self) -> None: if np.abs(self.mode_spec.angle_theta) > 0 and self.mode_spec.angle_rotation: _ = self._rotate_structures + def _validate_num_grid_points(self) -> None: + """Upper bound of the product of the number of grid points and the number of modes. The bound is very loose: subspace + size times the size of eigenvector can be indexed by a 32bit integer. + """ + num_cells, _, num_modes = self._num_cells_freqs_modes + relaxation_factor = 2 + if num_cells * (20 + 2 * num_modes) * relaxation_factor > 2**32 - 1: + raise SetupError( + "Too many grid points on the modal plane. Please reduce the modal plane size, apply a coarser grid, " + "or reduce the number of modes." + ) + @cached_property def normal_axis(self) -> Axis: """Axis normal to the mode plane.""" diff --git a/tidy3d/plugins/smatrix/ports/coaxial_lumped.py b/tidy3d/plugins/smatrix/ports/coaxial_lumped.py index 44655bffc0..6f3e14cc0a 100644 --- a/tidy3d/plugins/smatrix/ports/coaxial_lumped.py +++ b/tidy3d/plugins/smatrix/ports/coaxial_lumped.py @@ -192,7 +192,7 @@ def compute_coax_current(rin, rout, x, y): return CustomCurrentSource( center=center, - size=(self.outer_diameter, self.outer_diameter, 0), + size=size, source_time=source_time, name=self.name, interpolate=True, diff --git a/tidy3d/version.py b/tidy3d/version.py index a4f6f37051..2c159b7ebf 100644 --- a/tidy3d/version.py +++ b/tidy3d/version.py @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "2.9.0" +__version__ = "2.9.1" diff --git a/tidy3d/web/api/autograd/autograd.py b/tidy3d/web/api/autograd/autograd.py index af48d3eaa1..02a24d6527 100644 --- a/tidy3d/web/api/autograd/autograd.py +++ b/tidy3d/web/api/autograd/autograd.py @@ -22,6 +22,7 @@ ) from tidy3d.components.autograd.derivative_utils import DerivativeInfo from tidy3d.components.data.data_array import DataArray +from tidy3d.components.grid.grid_spec import GridSpec from tidy3d.exceptions import AdjointError from tidy3d.web.api.asynchronous import DEFAULT_DATA_DIR from tidy3d.web.api.asynchronous import run_async as run_async_webapi @@ -1070,10 +1071,14 @@ def postprocess_adj( sim_orig = sim_data_orig.simulation plane_eps = eps_fwd.monitor.geometry + sim_orig_grid_spec = GridSpec.from_grid(sim_orig.grid) + # permittivity without this structure structs_no_struct = list(sim_orig.structures) structs_no_struct.pop(structure_index) - sim_no_structure = sim_orig.updated_copy(structures=structs_no_struct) + sim_no_structure = sim_orig.updated_copy( + structures=structs_no_struct, monitors=[], sources=[], grid_spec=sim_orig_grid_spec + ) eps_no_structure_data = [ sim_no_structure.epsilon(box=plane_eps, coord_key="centers", freq=f) @@ -1086,6 +1091,8 @@ def postprocess_adj( structures=structs_inf_struct, medium=structure.medium, monitors=[], + sources=[], + grid_spec=sim_orig_grid_spec, ) eps_inf_structure_data = [ diff --git a/tidy3d/web/api/container.py b/tidy3d/web/api/container.py index fb5b1915bb..5d7e376020 100644 --- a/tidy3d/web/api/container.py +++ b/tidy3d/web/api/container.py @@ -601,6 +601,7 @@ def run(self, path_dir: str = DEFAULT_DATA_DIR) -> BatchData: """ self._check_path_dir(path_dir) self.upload() + self.to_file(self._batch_path(path_dir=path_dir)) self.start() self.monitor() return self.load(path_dir=path_dir) @@ -988,7 +989,6 @@ def load(self, path_dir: str = DEFAULT_DATA_DIR, replace_existing: bool = False) allowing one to load this :class:`Batch` later using ``batch = Batch.from_file()``. """ self._check_path_dir(path_dir=path_dir) - self.download(path_dir=path_dir, replace_existing=replace_existing) if self.jobs is None: raise DataError("Can't load batch results, hasn't been uploaded.") @@ -1010,6 +1010,8 @@ def load(self, path_dir: str = DEFAULT_DATA_DIR, replace_existing: bool = False) job_data = data[task_name] job.simulation._patch_data(data=job_data) + self.download(path_dir=path_dir, replace_existing=replace_existing) + return data def delete(self) -> None: