Source code for fdtd.lumped_elements

"""This module implement lumped elements."""
from __future__ import annotations

import logging
from functools import partial
from typing import Optional

import numpy as np

from .bases import FDTDElementBase
from .constants import FREESPACE_PERMITTIVITY as EPS_0
from .utils import BoundingBox, Direction

logger = logging.getLogger(__name__)


class LumpedElement(FDTDElementBase):
    """Base class for all lumped elements.

    Parameters
    ----------
    x_min : float
        Minimum x coordinate of the bounding box containing the lumped element.
    y_min : float
        Minimum y coordinate of the bounding box containing the lumped element.
    z_min : float
        Minimum z coordinate of the bounding box containing the lumped element.
    x_max : float
        Maximum x coordinate of the bounding box containing the lumped element.
    y_max : float
        Maximum y coordinate of the bounding box containing the lumped element.
    z_max : float
        Maximum z coordinate of the bounding box containing the lumped element.
    name : Optional[str]
        Name of the lumped element.
    direction : Direction
        Direction of the lumped element.
    """

    def __init__(
        self,
        x_min: float,
        y_min: float,
        z_min: float,
        x_max: float,
        y_max: float,
        z_max: float,
        name: Optional[str] = None,
        direction: Direction = Direction.Z,
    ):
        """Initialize the lumped element."""
        super().__init__()
        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.name = name if name else self._create_new_name()
        self.direction = direction

        self.bounding_box = BoundingBox(x_min, x_max, y_min, y_max, z_min,
                                        z_max)

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

    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="#ff0000")

        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 Resistor(LumpedElement): """Model of a resistor element. Parameters ---------- x_min : float Minimum x coordinate of the bounding box containing the resistor. y_min : float Minimum y coordinate of the bounding box containing the resistor. z_min : float Minimum z coordinate of the bounding box containing the resistor. x_max : float Maximum x coordinate of the bounding box containing the resistor. y_max : float Maximum y coordinate of the bounding box containing the resistor. z_max : float Maximum z coordinate of the bounding box containing the resistor. resistance : float Internal resistance of the resistor. """ def __init__( self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float, resistance: float = 50, ): """Initialize the object.""" super().__init__(x_min, y_min, z_min, x_max, y_max, z_max) self.resistance = resistance def __repr__(self): """Dev. string representation.""" return (f"{self.__class__.__name__}" f"[name={self.name}, resistance={self.resistance}, " 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}]") def attach_to_grid(self): """Attach object to grid.""" 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)), ) 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)), ) dx, dy, dz = self.grid.spacing dt = self.grid.dt r_s = self.resistance if self.direction == Direction.X: i_s = slice(self.idx_s[0], self.idx_e[0]) j_s = slice(self.idx_s[1], self.idx_e[1] + 1) k_s = slice(self.idx_s[2], self.idx_e[2] + 1) term = (dt*dx) / (r_s*dy*dz) elif self.direction == Direction.Y: i_s = slice(self.idx_s[0], self.idx_e[0] + 1) j_s = slice(self.idx_s[1], self.idx_e[1]) k_s = slice(self.idx_s[2], self.idx_e[2] + 1) term = (dt*dy) / (r_s*dx*dz) else: i_s = slice(self.idx_s[0], self.idx_e[0] + 1) j_s = slice(self.idx_s[1], self.idx_e[1] + 1) k_s = slice(self.idx_s[2], self.idx_e[2]) term = (dt*dz) / (r_s*dx*dy) eps = self.grid.eps_r[i_s, j_s, k_s, self.direction.value] * EPS_0 sigma_e = self.grid.sigma_e[i_s, j_s, k_s, self.direction.value] self.grid.c_ee[i_s, j_s, k_s, self.direction.value] = \ (2*eps - dt*sigma_e - term) / (2*eps + dt*sigma_e + term) self.grid.c_eh[i_s, j_s, k_s, self.direction.value] = \ (2*dt) / (2*eps + dt*sigma_e + term)
[docs]class Capacitor(LumpedElement): """Model of a capacitor element. Parameters ---------- x_min : float Minimum x coordinate of the bounding box containing the capacitor. y_min : float Minimum y coordinate of the bounding box containing the capacitor. z_min : float Minimum z coordinate of the bounding box containing the capacitor. x_max : float Maximum x coordinate of the bounding box containing the capacitor. y_max : float Maximum y coordinate of the bounding box containing the capacitor. z_max : float Maximum z coordinate of the bounding box containing the capacitor. capacitance : float Internal capacitance of the capacitor. """ def __init__( self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float, capacitance: float = 1e-9, ): """Initialize the object.""" 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.capacitance = capacitance def __repr__(self): """Dev. string representation.""" return (f"{self.__class__.__name__}" f"[name={self.name}, capacitance={self.capacitance}, " 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}]") def attach_to_grid(self): """Attach object to grid.""" 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)), ) 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)), ) dx, dy, dz = self.grid.spacing dt = self.grid.dt c_s = self.capacitance if self.direction == Direction.X: i_s = slice(self.idx_s[0], self.idx_e[0]) j_s = slice(self.idx_s[1], self.idx_e[1] + 1) k_s = slice(self.idx_s[2], self.idx_e[2] + 1) term = (2*c_s*dx) / (dy*dz) elif self.direction == Direction.Y: i_s = slice(self.idx_s[0], self.idx_e[0] + 1) j_s = slice(self.idx_s[1], self.idx_e[1]) k_s = slice(self.idx_s[2], self.idx_e[2] + 1) term = (2*c_s*dy) / (dx*dz) else: i_s = slice(self.idx_s[0], self.idx_e[0] + 1) j_s = slice(self.idx_s[1], self.idx_e[1] + 1) k_s = slice(self.idx_s[2], self.idx_e[2]) term = (2*c_s*dz) / (dx*dy) eps = self.grid.eps_r[i_s, j_s, k_s, self.direction.value] * EPS_0 sigma_e = self.grid.sigma_e[i_s, j_s, k_s, self.direction.value] self.grid.c_ee[i_s, j_s, k_s, self.direction.value] = \ (2*eps - dt*sigma_e + term) / (2*eps + dt*sigma_e + term) self.grid.c_eh[i_s, j_s, k_s, self.direction.value] = \ (2*dt) / (2*eps + dt*sigma_e + term)
[docs]class Inductor(LumpedElement): """Model of a inductor element. Parameters ---------- x_min : float Minimum x coordinate of the bounding box containing the inductor. y_min : float Minimum y coordinate of the bounding box containing the inductor. z_min : float Minimum z coordinate of the bounding box containing the inductor. x_max : float Maximum x coordinate of the bounding box containing the inductor. y_max : float Maximum y coordinate of the bounding box containing the inductor. z_max : float Maximum z coordinate of the bounding box containing the inductor. resistance : float Internal resistance of the inductor. capacitance : float Internal inductance of the inductor. """ def __init__(self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float, resistance: float = 50, inductance: float = 1e-6): """Initialize the object.""" 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.resistance = resistance self.inductance = inductance
class Diode(LumpedElement): """Implement a diode element.""" def __init__( self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float, resistance: float = 50, ): """Initialize the object.""" 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.resistance = resistance