Source code for fdtd.objects

"""This module implements the objects."""
from __future__ import annotations

import logging
from functools import partial
from typing import Optional, Union

import numpy as np

from .bases import FDTDElementBase
from .constants import PI
from .materials import Material
from .utils import BoundingBox

logger = logging.getLogger(__name__)


class Object(FDTDElementBase):
    """An object to be placed in the grid."""

    def __init__(self,
                 material: Union[str, Material],
                 name: Optional[str] = None):
        """Initialize the object."""
        super().__init__()

        if isinstance(material, str):
            self.material = Material.from_name(material)
        else:
            self.material = material

        self.name = name if name else self._create_new_name()

    def attach_to_grid(self):
        """Attach object to grid."""

    def plot_3d(self, ax, alpha=0.5):
        """Plot a brick and attach to an axis."""


[docs]class Brick(Object): """Implement a brick object.""" def __init__( self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float, material: Union[str, Material], name: Optional[str] = None, ): """Initialize the object.""" super().__init__(material, name) self.x_min = x_min self.y_min = y_min self.z_min = z_min self.x_max = x_max self.y_max = y_max self.z_max = z_max self.bounding_box = BoundingBox(x_min, x_max, y_min, y_max, z_min, z_max) def __repr__(self): """Dev. string representation.""" return (f"Brick[name={self.name}, " f"x_min={self.x_min}, y_min={self.y_min}, z_min={self.z_min}, " f"x_max={self.x_max}, y_max={self.y_max}, z_max={self.z_max}]")
[docs] def attach_to_grid(self): """Attach the material of an object to the grid.""" grid = self.grid bb = self.bounding_box slice_x = (grid._x_c >= bb.x_min) & (grid._x_c <= bb.x_max) slice_y = (grid._y_c >= bb.y_min) & (grid._y_c <= bb.y_max) slice_z = (grid._z_c >= bb.z_min) & (grid._z_c <= bb.z_max) I, J, K = np.ix_(slice_x, slice_y, slice_z) grid.cell_material[I, J, K, :] = [ self.material.eps_r, self.material.mu_r, self.material.sigma_e, self.material.sigma_m, ]
[docs] def attach_to_grid_zero_thinkness(self): """Attach the coeficients directly to the property grid.""" s = self.idx_s = ( np.argmin(np.abs(self.grid._x - self.x_min)), np.argmin(np.abs(self.grid._y - self.y_min)), np.argmin(np.abs(self.grid._z - self.z_min)), ) e = self.idx_e = ( np.argmin(np.abs(self.grid._x - self.x_max)), np.argmin(np.abs(self.grid._y - self.y_max)), np.argmin(np.abs(self.grid._z - self.z_max)), ) sigma_e = self.grid.sigma_e sigma_e_mat = self.material.sigma_e if s[0] == e[0]: sigma_e[s[0], s[1]:e[1], s[2]:e[2] + 1, 1] = sigma_e_mat sigma_e[s[0], s[1]:e[1] + 1, s[2]:e[2], 2] = sigma_e_mat elif s[1] == e[1]: sigma_e[s[0]:e[0], s[1], s[2]:e[2] + 1, 0] = sigma_e_mat sigma_e[s[0]:e[0] + 1, s[1], s[2]:e[2], 2] = sigma_e_mat elif s[2] == e[2]: sigma_e[s[0]:e[0], s[1]:e[1] + 1, s[2], 0] = sigma_e_mat sigma_e[s[0]:e[0] + 1, s[1]:e[1], s[2], 1] = sigma_e_mat
[docs] def plot_3d(self, ax, alpha: float = 0.5): """Plot a brick and attach to an axis.""" X, Y, Z = np.meshgrid( [self.x_min, self.x_max], [self.y_min, self.y_max], [self.z_min, self.z_max], ) plot = partial(ax.plot_surface, alpha=alpha, color=self.material.color) plot(X[:, :, 0], Y[:, :, 0], Z[:, :, 0]) plot(X[:, :, -1], Y[:, :, -1], Z[:, :, -1]) plot(X[:, 0, :], Y[:, 0, :], Z[:, 0, :]) plot(X[:, -1, :], Y[:, -1, :], Z[:, -1, :]) plot(X[0, :, :], Y[0, :, :], Z[0, :, :]) plot(X[-1, :, :], Y[-1, :, :], Z[-1, :, :])
[docs]class Sphere(Object): """Implement a brick object.""" def __init__( self, x_center: float, y_center: float, z_center: float, radius: float, material: Union[str, Material], name: Optional[str] = None, ): """Initialize the object.""" super().__init__(material, name) self.x_center = x_center self.y_center = y_center self.z_center = z_center self.radius = radius self.bounding_box = BoundingBox( x_center - radius, x_center + radius, y_center - radius, y_center + radius, z_center - radius, z_center + radius, ) def __repr__(self): """Dev. string representation.""" return (f"Sphere[name={self.name}, " f"x_c={self.x_center}, y_c={self.y_center}, " f"z_c={self.z_center}, r={self.radius}]")
[docs] def attach_to_grid(self): """Attach the material of an object to the grid.""" grid = self.grid bb = self.bounding_box slice_x = (grid._x_c > bb.x_min) & (grid._x_c < bb.x_max) slice_y = (grid._y_c > bb.y_min) & (grid._y_c < bb.y_max) slice_z = (grid._z_c > bb.z_min) & (grid._z_c < bb.z_max) I, J, K = np.ix_(slice_x, slice_y, slice_z) l_m_g = grid.cell_material[I, J, K, :] l_x_c, l_y_c, l_z_c = np.meshgrid(grid._x_c[slice_x], grid._y_c[slice_y], grid._z_c[slice_z]) mask = (l_x_c - self.x_center)**2 + (l_y_c - self.y_center)**2 + ( l_z_c - self.z_center)**2 <= self.radius**2 l_m_g[mask, :] = [ self.material.eps_r, self.material.mu_r, self.material.sigma_e, self.material.sigma_m, ] grid.cell_material[I, J, K, :] = l_m_g
[docs] def plot_3d(self, ax, alpha=0.5): """Plot a brick and attach to an axis.""" u = np.linspace(0, 2 * PI, 100) v = np.linspace(0, PI, 100) x = self.x_center + self.radius * np.outer(np.cos(u), np.sin(v)) y = self.y_center + self.radius * np.outer(np.sin(u), np.sin(v)) z = self.z_center + self.radius * np.outer(np.ones(np.size(u)), np.cos(v)) ax.plot_surface(x, y, z, alpha=alpha, color=self.material.color)