Beispiel #1
0
    def draw_slab(self, dir_slab: Direction or float, center: float,
                  thickness: float, eps: float or np.ndarray):
        """
        Draw a slab
        """

        # Validating input arguments
        if isinstance(dir_slab, Direction):
            dir_slab = dir_slab.value
        elif not is_scalar(dir_slab):
            raise GridError('Invalid slab direction')
        elif not dir_slab in range(3):
            raise GridError('Invalid slab direction')

        if not is_scalar(center):
            raise GridError('Invalid slab center')

        if is_scalar(eps):
            eps = np.ones(self.shifts.shape[0]) * eps

        if eps.ndim != 1 or eps.size != self.shifts.shape[0]:
            raise GridError(
                'Invalid permittivity - must be a scalar or vector of length equalling number of grids'
            )

        dir_slab_par = np.delete(range(3), dir_slab)
        cuboid_cen = np.array(
            [self.center[a] if a != dir_slab else center for a in range(3)])
        cuboid_extent = np.array([1.5*np.abs(self.exyz[a][-1]-self.exyz[a][0]) if a !=dir_slab \
                                  else thickness for a in range(3)])
        self.draw_cuboid(cuboid_cen, cuboid_extent, eps)
Beispiel #2
0
    def draw_cylinder(self, center: np.ndarray, radius: float,
                      thickness: float, num_points: int, eps: float
                      or np.ndarray):
        """
        Draw a cylinder with permittivity epsilon. By default, the axis of the cylinder
        is assumed to be along the extrusion direction
        
        """
        center = np.array(center)
        # Validating input parameters
        if center.ndim != 1 or center.size != 3:
            raise GridError('Invalid center coordinate')

        if is_scalar(eps):
            eps = np.ones(self.shifts.shape[0]) * eps

        if eps.ndim != 1 or eps.size != self.shifts.shape[0]:
            raise GridError(
                'Invalid permittvity - must be scalar or vector of length equalling number of grids'
            )

        if not is_scalar(thickness):
            raise GridError('Invalid thickness')

        if not is_scalar(num_points):
            raise GridError('Invalid number of points on the cylinder')

        # Approximating the drawn cylinder with a polygon with number of vertices = num_points
        theta = np.linspace(0, 2.0 * np.pi, num_points)
        x = radius * np.sin(theta)
        y = radius * np.cos(theta)
        polygon = np.vstack((x, y)).T

        # Drawing polygon
        self.draw_polygon(center, polygon, thickness, eps)
Beispiel #3
0
    def draw_cuboid(self, center: np.ndarray, extent: np.ndarray, eps: float
                    or List[float]):
        """
        Draw a cuboid with permittivity epsilon
        """

        center = np.array(center)
        extent = np.array(extent)
        # Validating input parameters
        if center.ndim != 1 or center.size != 3:
            raise GridError('Invalid center coordinate')

        if extent.ndim != 1 or extent.size != 3:
            raise GridError('Invalid cuboid lengths')

        if is_scalar(eps):
            eps = np.ones(self.shifts.shape[0]) * eps
        if eps.ndim != 1 or eps.size != self.shifts.shape[0]:
            raise GridError(
                'Invalid permittivity - must be scalar or vector of length equalling number of grids'
            )

        # Calculating the polygon corresponding to the drawn cuboid
        polygon = 0.5 * np.array(
            [[-extent[self.planar_dir[0]], extent[self.planar_dir[1]]],
             [extent[self.planar_dir[0]], extent[self.planar_dir[1]]],
             [extent[self.planar_dir[0]], -extent[self.planar_dir[1]]],
             [-extent[self.planar_dir[0]], -extent[self.planar_dir[1]]]],
            dtype=float)

        thickness = extent[self.ext_dir]

        # Drawing polygon
        self.draw_polygon(center, polygon, thickness, eps)
Beispiel #4
0
    def draw_polygon(self, center: np.ndarray, polygon: np.ndarray,
                     thickness: float, eps: float or List[float]):
        """
        Draws a polygon with coordinates in polygon and thickness
        Note on order of coordinates in polygon - 
        If ext_dir = x, then polygon has coordinates of form (y,z)
           ext_dir = y, then polygon has coordinates of form (x,y)
           ext_dir = z, then polygon has coordinates of form (x,y)
        """

        center = np.array(center)
        polygon = np.array(polygon)
        # Validating input arguments
        if polygon.ndim != 2 or polygon.shape[1] != 2:
            raise GridError(
                'Invalid format for specifying polygon - must be a Nx2 array')

        if polygon.shape[0] <= 2:
            raise GridError(
                'Malformed Polygon - must contain more than two points')

        if center.ndim != 1 or center.size != 3:
            raise GridError('Invalid format for the polygon center')

        if (not is_scalar(thickness)) or thickness <= 0:
            raise GridError('Invalid thickness')

        if is_scalar(eps):
            eps = np.ones(self.shifts.shape[0]) * eps
        elif eps.ndim != 1 and eps.size != self.shifts.shape[0]:
            raise GridErro(
                'Invalid permittivity - must be scalar or vector of length equalling number of grids'
            )
        # Translating polygon by its center
        polygon_translated = polygon + np.tile(center[self.planar_dir],
                                               (polygon.shape[0], 1))
        self.list_polygons.append((polygon_translated, eps))

        # Adding the z-coordinates of the z-coordinates of the added layers
        self.list_z.append([
            center[self.ext_dir] - 0.5 * thickness,
            center[self.ext_dir] + 0.5 * thickness
        ])
Beispiel #5
0
    def fill_cuboid(self, fill_dir: Direction, fill_pol: int,
                    surf_center: np.ndarray, surf_extent: np.ndarray,
                    eps: float or np.ndarray):
        '''
        INPUTS:
        1. surf_extent - array of size 2 corresponding to the extent of the surface. If the fill direction
        is x, then the two elements correspond to y,z, if it is y then x,z and if it is z then x,y
        '''
        surf_center = np.array(surf_center)
        surf_extent = np.array(surf_extent)

        # Validating input arguments
        if isinstance(fill_dir, Direction):
            fill_dir = fill_dir.value
        elif not is_scalar(fill_dir):
            raise GridError('Invalid slab direction')
        elif not dir_slab in range(3):
            raise GridError('Invalid slab direction')

        if not is_scalar(fill_pol):
            raise GridError('Invalid polarity')
        if not fill_pol in [-1, 1]:
            raise GridError('Invalid polarity')

        if surf_center.ndim != 1 or surf_center.size != 3:
            raise GridError('Invalid surface center')

        if surf_extent.ndim != 1 or surf_extent.size != 2:
            raise GridError('Invalid surface extent')

        edge_lim = self.exyz[fill_dir][0] if fill_pol == -1 else self.exyz[
            fill_dir][-1]
        cuboid_extent = np.insert(surf_extent, fill_dir,
                                  2 * np.abs(edge_lim - surf_center[fill_dir]))


        cuboid_center = np.array([surf_center[a] if a != fill_dir else \
                                     (surf_center[a]+0.5*fill_pol*cuboid_extent[a]) for a in range(3)])

        self.draw_cuboid(cuboid_center, cuboid_extent, eps)
Beispiel #6
0
    def fill_slab(self, fill_dir: Direction, fill_pol: int, surf_center: float,
                  eps: float or np.ndarray):

        # Validating input arguments
        if isinstance(fill_dir, Direction):
            fill_dir = fill_dir.value
        elif not is_scalar(fill_dir):
            raise GridError('Invalid slab direction')
        elif not dir_slab in range(3):
            raise GridError('Invalid slab direction')

        if not is_scalar(fill_pol):
            raise GridError('Invalid polarity')
        if not fill_pol in [-1, 1]:
            raise GridError('Invalid polarity')

        if not is_scalar(surf_center):
            raise GridError('Invalid surface center')

        edge_lim = self.exyz[fill_dir][0] if fill_pol == -1 else self.exyz[
            fill_dir][-1]
        slab_thickness = 2 * np.abs(edge_lim - surf_center)
        slab_center = surf_center + 0.5 * fill_pol * slab_thickness
        self.draw_slab(fill_dir, slab_center, slab_thickness, eps)
Beispiel #7
0
    def get_slice(self,
                  surface_normal: Direction or int,
                  center: float,
                  which_shifts: int = 0,
                  sample_period: int = 1) -> np.ndarray:
        """
            Retrieve a slice of a grid.
            Interpolates if given a position between two planes.

            :param surface_normal: Axis normal to the plane we're displaying. Can be a Direction or
             integer in range(3)
            :param center: Scalar specifying position along surface_normal axis.
            :param which_shifts: Which grid to display. Default is the first grid (0).
            :param sample_period: Period for down-sampling the image. Default 1 (disabled)
            :return Array containing the portion of the grid.
        """
        if not is_scalar(center) and np.isreal(center):
            raise GridError('center must be a real scalar')

        sp = round(sample_period)
        if sp <= 0:
            raise GridError('sample_period must be positive')

        if not is_scalar(which_shifts) or which_shifts < 0:
            raise GridError('Invalid which_shifts')

        # Turn surface_normal into its integer representation
        if isinstance(surface_normal, Direction):
            surface_normal = surface_normal.value
        if surface_normal not in range(3):
            raise GridError('Invalid surface_normal direction')

        surface = np.delete(range(3), surface_normal)

        # Extract indices and weights of planes
        center3 = np.insert([0, 0], surface_normal, (center, ))
        center_index = self.pos2ind(center3,
                                    which_shifts,
                                    round_ind=False,
                                    check_bounds=False)[surface_normal]
        centers = np.unique([floor(center_index),
                             ceil(center_index)]).astype(int)
        if len(centers) == 2:
            fpart = center_index - floor(center_index)
            w = [1 - fpart, fpart]  # longer distance -> less weight
        else:
            w = [1]

        c_min, c_max = (self.xyz[surface_normal][i] for i in [0, -1])
        if center < c_min or center > c_max:
            raise GridError(
                'Coordinate of selected plane must be within simulation domain'
            )

        # Extract grid values from planes above and below visualized slice
        sliced_grid = zeros(self.shape[surface])
        for ci, weight in zip(centers, w):
            s = tuple(ci if a == surface_normal else np.s_[::sp]
                      for a in range(3))
            sliced_grid += weight * self.grids[which_shifts][tuple(s)]

        # Remove extra dimensions
        sliced_grid = np.squeeze(sliced_grid)

        return sliced_grid
Beispiel #8
0
    def __init__(self,
                 pixel_edge_coordinates: List[np.ndarray],
                 ext_dir: Direction = Direction.z,
                 shifts: np.ndarray or List = Yee_Shifts_E,
                 comp_shifts: np.ndarray or List = Yee_Shifts_H,
                 initial: float or np.ndarray or List[float]
                 or List[np.ndarray] = (1.0, ) * 3,
                 num_grids: int = None,
                 periodic: bool or List[bool] = False):

        # Backgrdound permittivity and fraction of background permittivity in the grid
        self.grids_bg = []  # type: List[np.ndarray]
        self.frac_bg = []  # type: List[np.ndarray]

        # [[x0 y0 z0], [x1 y1 z1], ...] offsets for primary grid 0,1,...
        self.exyz = [np.unique(pixel_edge_coordinates[i]) for i in range(3)]
        for i in range(3):
            if len(self.exyz[i]) != len(pixel_edge_coordinates[i]):
                warnings.warn(
                    'Dimension {} had duplicate edge coordinates'.format(i))

        if is_scalar(periodic):
            self.periodic = [periodic] * 3
        else:
            self.periodic = [False] * 3

        self.shifts = np.array(shifts, dtype=float)
        self.comp_shifts = np.array(comp_shifts, dtype=float)
        if self.shifts.shape[1] != 3:
            GridError(
                'Misshapen shifts on the primary grid; second axis size should be 3,'
                ' shape is {}'.format(self.shifts.shape))
        if self.comp_shifts.shape[1] != 3:
            GridError(
                'Misshapen shifts on the complementary grid: second axis size should be 3,'
                ' shape is {}'.format(self.comp_shifts.shape))
        if self.comp_shifts.shape[0] != self.shifts.shape[0]:
            GridError(
                'Inconsistent number of shifts in the primary and complementary grid'
            )
        if not ((self.shifts >= 0).all() and (self.comp_shifts >= 0).all()):
            GridError(
                'Shifts are required to be non-negative for both primary and complementary grid'
            )

        num_shifts = self.shifts.shape[0]
        if num_grids is None:
            num_grids = num_shifts
        elif num_grids > num_shifts:
            raise GridError('Number of grids exceeds number of shifts (%u)' %
                            num_shifts)

        grids_shape = hstack((num_grids, self.shape))
        if is_scalar(initial):
            self.grids_bg = np.full(grids_shape, initial, dtype=complex)
        else:
            if len(initial) < num_grids:
                raise GridError('Too few initial grids specified!')

            self.grids_bg = [None] * num_grids
            for i in range(num_grids):
                if is_scalar(initial[i]):
                    if initial[i] is not None:
                        self.grids_bg[i] = np.full(self.shape,
                                                   initial[i],
                                                   dtype=complex)
                else:
                    if not np.array_equal(initial[i].shape, self.shape):
                        raise GridError(
                            'Initial grid sizes must match given coordinates')
                    self.grids_bg[i] = initial[i]

        if isinstance(ext_dir, Direction):
            self.ext_dir = ext_dir.value
        elif is_scalar(ext_dir):
            if ext_dir in range(3):
                self.ext_dir = ext_dir
            else:
                raise GridError('Invalid extrusion direction')
        else:
            raise GridError('Invalid extrusion direction')

        self.grids = np.full(
            grids_shape, 0.0,
            dtype=complex)  # contains the rendering of objects on the grid
        self.frac_bg = np.full(
            grids_shape, 1.0,
            dtype=complex)  # contains the fraction of background permittivity
        self.planar_dir = np.delete(range(3), self.ext_dir)
        self.list_polygons = [
        ]  # List of polygons corresponding to each block specified by user
        self.layer_polygons = [
        ]  # List of polygons after bifurcating extrusion direction into layers
        self.reduced_layer_polygons = [
        ]  # List of polygons after removing intersections

        self.list_z = [
        ]  # List of z coordinates of the different blocks specified by user
        self.layer_z = [
        ]  # List of z coordinates of the different distinct layers