Source code for fdtd.objects

""" The objects to place in the grid.

Objects define all the regions in the grid with a modified update equation,
such as for example regions with anisotropic permittivity etc.

Available Objects:
 - Object
 - AnisotropicObject

"""

## Imports

# typing
from .typing_ import Tensorlike, ListOrSlice

# relative
from .grid import Grid
from .backend import backend as bd
from . import constants as const


## Object
[docs]class Object: """ An object to place in the grid """
[docs] def __init__(self, permittivity: Tensorlike, name: str = None): """ Args: permittivity: permittivity tensor name: name of the object (will become available as attribute to the grid) """ self.grid = None self.name = name self.permittivity = bd.array(permittivity)
def _register_grid( self, grid: Grid, x: ListOrSlice, y: ListOrSlice, z: ListOrSlice ): """Register the object to the grid Args: grid: the grid to register the object into x: the x-location of the object in the grid y: the y-location of the object in the grid z: the z-location of the object in the grid """ self.grid = grid self.grid.objects.append(self) if self.permittivity.dtype is bd.complex().dtype: self.grid.promote_dtypes_to_complex() if self.name is not None: if not hasattr(grid, self.name): setattr(grid, self.name, self) else: raise ValueError( f"The grid already has an attribute with name {self.name}" ) self.x = self._handle_slice(x, max_index=self.grid.Nx) self.y = self._handle_slice(y, max_index=self.grid.Ny) self.z = self._handle_slice(z, max_index=self.grid.Nz) self.Nx = abs(self.x.stop - self.x.start) self.Ny = abs(self.y.stop - self.y.start) self.Nz = abs(self.z.stop - self.z.start) # set the permittivity of the object if bd.is_array(self.permittivity) and len(self.permittivity.shape) == 3: self.permittivity = self.permittivity[:, :, :, None] self.inverse_permittivity = ( bd.ones((self.Nx, self.Ny, self.Nz, 3),dtype=self.permittivity.dtype) / self.permittivity ) # set the permittivity values of the object at its border to be equal # to the grid permittivity. This way, the object is made symmetric. if self.Nx > 1: self.inverse_permittivity[-1, :, :, 0] = self.grid.inverse_permittivity[ -1, self.y, self.z, 0 ] if self.Ny > 1: self.inverse_permittivity[:, -1, :, 1] = self.grid.inverse_permittivity[ self.x, -1, self.z, 1 ] if self.Nz > 1: self.inverse_permittivity[:, :, -1, 2] = self.grid.inverse_permittivity[ self.x, self.y, -1, 2 ] self.grid.inverse_permittivity[self.x, self.y, self.z] = 0 def _handle_slice(self, s: ListOrSlice, max_index: int = None) -> slice: if isinstance(s, list): if len(s) == 1: return slice(s[0], s[0] + 1, None) raise IndexError( "One can only use slices or single indices to index the grid for an Object" ) if isinstance(s, slice): start, stop, step = s.start, s.stop, s.step if step is not None and step != 1: raise IndexError( "Can only use slices with unit step to index the grid for an Object" ) if start is None: start = 0 if start < 0: start = max_index + start if stop is None: stop = max_index if stop < 0: stop = max_index + stop return slice(start, stop, None) raise ValueError("Invalid grid indexing used for object")
[docs] def update_E(self, curl_H): """custom update equations for inside the object Args: curl_H: the curl of magnetic field in the grid. """ loc = (self.x, self.y, self.z) self.grid.E[loc] = self.grid.E[loc] +( self.grid.courant_number * self.inverse_permittivity * curl_H[loc] )
[docs] def update_H(self, curl_E): """custom update equations for inside the object Args: curl_E: the curl of electric field in the grid. """
# def promote_dtypes_to_complex(self): # self.E = self.E.astype(bd.complex) # self.H = self.H.astype(bd.complex) def __repr__(self): return f"{self.__class__.__name__}(name={repr(self.name)})" def __str__(self): s = " " + repr(self) + "\n" def _handle_slice(s): return ( str(s) .replace("slice(", "") .replace(")", "") .replace(", ", ":") .replace("None", "") ) x = _handle_slice(self.x) y = _handle_slice(self.y) z = _handle_slice(self.z) s += f" @ x={x}, y={y}, z={z}".replace(":,", ",") if s[-1] == ":": s = s[:-1] return s + "\n"
[docs]class AbsorbingObject(Object): """ An absorbing object takes conductivity into account """
[docs] def __init__( self, permittivity: Tensorlike, conductivity: Tensorlike, name: str = None ): """ Args: permittivity: permittivity tensor conductivity: conductivity tensor (will introduce the loss) name: name of the object (will become available as attribute to the grid) """ super().__init__(permittivity, name) self.conductivity = bd.array(conductivity)
def _register_grid( self, grid: Grid, x: slice = None, y: slice = None, z: slice = None ): """Register a grid to the object Args: grid: the grid to register the object into x: the x-location of the object in the grid y: the y-location of the object in the grid z: the z-location of the object in the grid """ super()._register_grid(grid=grid, x=x, y=y, z=z) conductivity = bd.asarray(self.conductivity) while conductivity.ndim < self.inverse_permittivity.ndim: conductivity = conductivity[..., None] self.conductivity = bd.broadcast_to( conductivity, self.inverse_permittivity.shape ) self.absorption_factor = ( 0.5 * self.grid.courant_number * self.inverse_permittivity * self.conductivity * self.grid.grid_spacing * const.eta0 )
[docs] def update_E(self, curl_H): """custom update equations for inside the absorbing object Args: curl_H: the curl of magnetic field in the grid. """ loc = (self.x, self.y, self.z) self.grid.E[loc] *= (1 - self.absorption_factor) / (1 + self.absorption_factor) self.grid.E[loc] += ( self.grid.courant_number * self.inverse_permittivity * curl_H[loc] / (1 + self.absorption_factor) )
[docs] def update_H(self, curl_E): """custom update equations for inside the absorbing object Args: curl_E: the curl of electric field in the grid. """
[docs]class AnisotropicObject(Object): """ An object with anisotropic permittivity tensor """ def _register_grid( self, grid: Grid, x: slice = None, y: slice = None, z: slice = None ): """Register a grid to the object Args: grid: the grid to register the object into x: the x-location of the object in the grid y: the y-location of the object in the grid z: the z-location of the object in the grid """ super()._register_grid(grid=grid, x=x, y=y, z=z) eye = bd.zeros((self.Nx * self.Ny * self.Nz, 3, 3)) eye[:, range(3), range(3)] = 1.0 self.inverse_permittivity = bd.reshape( bd.reshape(self.inverse_permittivity, (-1, 1, 3)) * eye, (self.Nx, self.Ny, self.Nz, 3, 3), )
[docs] def update_E(self, curl_H): """custom update equations for inside the anisotropic object Args: curl_H: the curl of magnetic field in the grid. """ loc = (self.x, self.y, self.z) self.grid.E[loc] += bd.reshape( self.grid.courant_number * bd.bmm( bd.reshape(self.inverse_permittivity, (-1, 3, 3)), bd.reshape(curl_H[loc], (-1, 3, 1)), ), (self.Nx, self.Ny, self.Nz, 3), )
[docs] def update_H(self, curl_E): """custom update equations for inside the anisotropic object Args: curl_E: the curl of electric field in the grid. """