Source code for tidy3d.components.base_sim.simulation
"""Abstract base for defining simulation classes of different solvers"""from__future__importannotationsfromabcimportABC,abstractmethodfromtypingimportTuplefrommathimportiscloseimportpydantic.v1aspdfrom.monitorimportAbstractMonitorfrom..baseimportcached_property,skip_if_fields_missingfrom..validatorsimportassert_unique_names,assert_objects_in_sim_boundsfrom..geometry.baseimportBoxfrom..typesimportAx,Bound,Axis,Symmetry,TYPE_TAG_STRfrom..structureimportStructurefrom..vizimportadd_ax_if_none,equal_aspectfrom..sceneimportScenefrom..mediumimportMedium,MediumType3Dfrom..vizimportPlotParams,plot_params_symmetryfrom...versionimport__version__from...exceptionsimportTidy3dKeyErrorfrom...logimportlog
[docs]classAbstractSimulation(Box,ABC):"""Base class for simulation classes of different solvers."""medium:MediumType3D=pd.Field(Medium(),title="Background Medium",description="Background medium of simulation, defaults to vacuum if not specified.",discriminator=TYPE_TAG_STR,)""" Background medium of simulation, defaults to vacuum if not specified. """structures:Tuple[Structure,...]=pd.Field((),title="Structures",description="Tuple of structures present in simulation. ""Note: Structures defined later in this list override the ""simulation material properties in regions of spatial overlap.",)""" Tuple of structures present in simulation. Structures defined later in this list override the simulation material properties in regions of spatial overlap. Example ------- Simple application reference: .. code-block:: python Simulation( ... structures=[ Structure( geometry=Box(size=(1, 1, 1), center=(0, 0, 0)), medium=Medium(permittivity=2.0), ), ], ... ) """symmetry:Tuple[Symmetry,Symmetry,Symmetry]=pd.Field((0,0,0),title="Symmetries",description="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. ",)sources:Tuple[None,...]=pd.Field((),title="Sources",description="Sources in the simulation.",)boundary_spec:None=pd.Field(None,title="Boundaries",description="Specification of boundary conditions.",)monitors:Tuple[None,...]=pd.Field((),title="Monitors",description="Monitors in the simulation. ",)grid_spec:None=pd.Field(None,title="Grid Specification",description="Specifications for the simulation grid.",)version:str=pd.Field(__version__,title="Version",description="String specifying the front end version number.",)""" Validating setup """# make sure all names are unique_unique_monitor_names=assert_unique_names("monitors")_unique_structure_names=assert_unique_names("structures")_unique_source_names=assert_unique_names("sources")_monitors_in_bounds=assert_objects_in_sim_bounds("monitors")_structures_in_bounds=assert_objects_in_sim_bounds("structures",error=False)@pd.validator("structures",always=True)@skip_if_fields_missing(["size","center"])def_structures_not_at_edges(cls,val,values):"""Warn if any structures lie at the simulation boundaries."""ifvalisNone:returnvalsim_box=Box(size=values.get("size"),center=values.get("center"))sim_bound_min,sim_bound_max=sim_box.boundssim_bounds=list(sim_bound_min)+list(sim_bound_max)withlogasconsolidated_logger:foristruct,structureinenumerate(val):struct_bound_min,struct_bound_max=structure.geometry.boundsstruct_bounds=list(struct_bound_min)+list(struct_bound_max)forsim_val,struct_valinzip(sim_bounds,struct_bounds):ifisclose(sim_val,struct_val):consolidated_logger.warning(f"Structure at 'structures[{istruct}]' has bounds that extend exactly ""to simulation edges. This can cause unexpected behavior. ""If intending to extend the structure to infinity along one dimension, ""use td.inf as a size variable instead to make this explicit.",custom_loc=["structures",istruct],)returnval""" Post-init validators """def_post_init_validators(self)->None:"""Call validators taking z`self` that get run after init."""_=self.scene
[docs]defvalidate_pre_upload(self)->None:"""Validate the fully initialized simulation is ok for upload to our servers."""pass
""" Accounting """@cached_propertydefscene(self)->Scene:"""Scene instance associated with the simulation."""returnScene(medium=self.medium,structures=self.structures)
[docs]defget_monitor_by_name(self,name:str)->AbstractMonitor:"""Return monitor named 'name'."""formonitorinself.monitors:ifmonitor.name==name:returnmonitorraiseTidy3dKeyError(f"No monitor named '{name}'")
@cached_propertydefsimulation_bounds(self)->Bound:"""Simulation bounds including auxiliary boundary zones such as PML layers."""# in this default implementation we just take self.bounds# this should be changed in different solvers depending on whether automatic extensions# (like pml) are presentreturnself.bounds@cached_propertydefsimulation_geometry(self)->Box:"""The entire simulation domain including auxiliary boundary zones such as PML layers. It is identical to ``Simulation.geometry`` in the absence of such auxiliary zones. """rmin,rmax=self.simulation_boundsreturnBox.from_bounds(rmin=rmin,rmax=rmax)@cached_propertydefsimulation_structure(self)->Structure:"""Returns structure representing the domain of the simulation. This differs from ``Simulation.scene.background_structure`` in that it has finite extent."""returnStructure(geometry=self.simulation_geometry,medium=self.medium)
[docs]@equal_aspect@add_ax_if_nonedefplot(self,x:float=None,y:float=None,z:float=None,ax:Ax=None,source_alpha:float=None,monitor_alpha:float=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,**patch_kwargs,)->Ax:"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. source_alpha : float = None Opacity of the sources. If ``None``, uses Tidy3d default. monitor_alpha : float = None Opacity of the monitors. If ``None``, uses Tidy3d default. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """hlim,vlim=Scene._get_plot_lims(bounds=self.simulation_bounds,x=x,y=y,z=z,hlim=hlim,vlim=vlim)ax=self.scene.plot_structures(ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim)ax=self.plot_sources(ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim,alpha=source_alpha)ax=self.plot_monitors(ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim,alpha=monitor_alpha)ax=Scene._set_plot_bounds(bounds=self.simulation_bounds,ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim)ax=self.plot_boundaries(ax=ax,x=x,y=y,z=z)ax=self.plot_symmetries(ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnax
[docs]@equal_aspect@add_ax_if_nonedefplot_sources(self,x:float=None,y:float=None,z:float=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,alpha:float=None,ax:Ax=None,)->Ax:"""Plot each of simulation's sources on a plane defined by one nonzero x,y,z coordinate. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. alpha : float = None Opacity of the sources, If ``None`` uses Tidy3d default. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """bounds=self.boundsforsourceinself.sources:ax=source.plot(x=x,y=y,z=z,alpha=alpha,ax=ax,sim_bounds=bounds)ax=Scene._set_plot_bounds(bounds=self.simulation_bounds,ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnax
[docs]@equal_aspect@add_ax_if_nonedefplot_monitors(self,x:float=None,y:float=None,z:float=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,alpha:float=None,ax:Ax=None,)->Ax:"""Plot each of simulation's monitors on a plane defined by one nonzero x,y,z coordinate. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. alpha : float = None Opacity of the sources, If ``None`` uses Tidy3d default. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """bounds=self.boundsformonitorinself.monitors:ax=monitor.plot(x=x,y=y,z=z,alpha=alpha,ax=ax,sim_bounds=bounds)ax=Scene._set_plot_bounds(bounds=self.simulation_bounds,ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnax
[docs]@equal_aspect@add_ax_if_nonedefplot_symmetries(self,x:float=None,y:float=None,z:float=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,ax:Ax=None,)->Ax:"""Plot each of simulation's symmetries on a plane defined by one nonzero x,y,z coordinate. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """normal_axis,_=Box.parse_xyz_kwargs(x=x,y=y,z=z)forsym_axis,sym_valueinenumerate(self.symmetry):ifsym_value==0orsym_axis==normal_axis:continuesym_box=self._make_symmetry_box(sym_axis=sym_axis)plot_params=self._make_symmetry_plot_params(sym_value=sym_value)ax=sym_box.plot(x=x,y=y,z=z,ax=ax,**plot_params.to_kwargs())ax=Scene._set_plot_bounds(bounds=self.simulation_bounds,ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnax
def_make_symmetry_plot_params(self,sym_value:Symmetry)->PlotParams:"""Make PlotParams for symmetry."""plot_params=plot_params_symmetry.copy()ifsym_value==1:plot_params=plot_params.copy(update={"facecolor":"lightsteelblue","edgecolor":"lightsteelblue","hatch":"++"})elifsym_value==-1:plot_params=plot_params.copy(update={"facecolor":"goldenrod","edgecolor":"goldenrod","hatch":"--"})returnplot_paramsdef_make_symmetry_box(self,sym_axis:Axis)->Box:"""Construct a :class:`.Box` representing the symmetry to be plotted."""rmin,rmax=(list(bound)forboundinself.simulation_bounds)rmax[sym_axis]=(rmin[sym_axis]+rmax[sym_axis])/2returnBox.from_bounds(rmin,rmax)
[docs]@abstractmethod@equal_aspect@add_ax_if_nonedefplot_boundaries(self,x:float=None,y:float=None,z:float=None,ax:Ax=None,**kwargs,)->Ax:"""Plot the simulation boundary conditions as lines on a plane defined by one nonzero x,y,z coordinate. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. **kwargs Optional keyword arguments passed to the matplotlib ``LineCollection``. For details on accepted values, refer to `Matplotlib's documentation <https://tinyurl.com/2p97z4cn>`_. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """
[docs]@equal_aspect@add_ax_if_nonedefplot_structures(self,x:float=None,y:float=None,z:float=None,ax:Ax=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,)->Ax:"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """hlim_new,vlim_new=Scene._get_plot_lims(bounds=self.simulation_bounds,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnself.scene.plot_structures(x=x,y=y,z=z,ax=ax,hlim=hlim_new,vlim=vlim_new)
[docs]@equal_aspect@add_ax_if_nonedefplot_structures_eps(self,x:float=None,y:float=None,z:float=None,freq:float=None,alpha:float=None,cbar:bool=True,reverse:bool=False,ax:Ax=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,)->Ax:"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. freq : float = None Frequency to evaluate the relative permittivity of all mediums. If not specified, evaluates at infinite frequency. reverse : bool = False If ``False``, the highest permittivity is plotted in black. If ``True``, it is plotteed in white (suitable for black backgrounds). cbar : bool = True Whether to plot a colorbar for the relative permittivity. alpha : float = None Opacity of the structures being plotted. Defaults to the structure default alpha. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """hlim,vlim=Scene._get_plot_lims(bounds=self.simulation_bounds,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnself.scene.plot_structures_eps(freq=freq,cbar=cbar,alpha=alpha,ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim,reverse=reverse,)
[docs]@equal_aspect@add_ax_if_nonedefplot_structures_heat_conductivity(self,x:float=None,y:float=None,z:float=None,alpha:float=None,cbar:bool=True,reverse:bool=False,ax:Ax=None,hlim:Tuple[float,float]=None,vlim:Tuple[float,float]=None,)->Ax:"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. Parameters ---------- x : float = None position of plane in x direction, only one of x, y, z must be specified to define plane. y : float = None position of plane in y direction, only one of x, y, z must be specified to define plane. z : float = None position of plane in z direction, only one of x, y, z must be specified to define plane. freq : float = None Frequency to evaluate the relative permittivity of all mediums. If not specified, evaluates at infinite frequency. reverse : bool = False If ``False``, the highest permittivity is plotted in black. If ``True``, it is plotteed in white (suitable for black backgrounds). cbar : bool = True Whether to plot a colorbar for the relative permittivity. alpha : float = None Opacity of the structures being plotted. Defaults to the structure default alpha. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. hlim : Tuple[float, float] = None The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. Returns ------- matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """hlim,vlim=Scene._get_plot_lims(bounds=self.simulation_bounds,x=x,y=y,z=z,hlim=hlim,vlim=vlim)returnself.scene.plot_structures_heat_conductivity(cbar=cbar,alpha=alpha,ax=ax,x=x,y=y,z=z,hlim=hlim,vlim=vlim,reverse=reverse,)
[docs]@classmethoddeffrom_scene(cls,scene:Scene,**kwargs)->AbstractSimulation:"""Create a simulation from a :class:.`Scene` instance. Must provide additional parameters to define a valid simulation (for example, ``size``, ``run_time``, ``grid_spec``, etc). Parameters ---------- scene : :class:.`Scene` Scene containing structures information. **kwargs Other arguments """returncls(structures=scene.structures,medium=scene.medium,**kwargs,)