def solve(self, omega: complex, dxes: fdfd_tools.GridSpacing, J: np.ndarray, epsilon: np.ndarray, pml_layers: Optional[fdfd_tools.PmlLayers] = None, mu: np.ndarray = None, pec: np.ndarray = None, pmc: np.ndarray = None, pemc: np.ndarray = None, bloch_vec: Optional[np.ndarray] = None, symmetry: Optional[np.ndarray] = None, adjoint: bool = False, E0: np.ndarray = None): if (symmetry is not None) and np.any(symmetry): raise NotImplementedError("`symmetry` is not implemented.") if (pemc is not None) and np.any(pemc): raise NotImplementedError("`pemc` is not implemented.") if bloch_vec is None: bloch_vec = np.zeros(3) dxes = fdfd_tools.grid.apply_scpml(dxes, pml_layers, omega) b0 = -1j * omega * J A0 = operators.e_full(omega, dxes, epsilon=epsilon, mu=mu, pec=pec, pmc=pmc, bloch_vec=bloch_vec) Pl, Pr = operators.e_full_preconditioners(dxes) if adjoint: A = (Pl @ A0 @ Pr).H b = Pr.H @ b0 else: A = Pl @ A0 @ Pr b = Pl @ b0 x = self.solve_matrix_equation(A.astype(np.complex128).tocsr(), b) if adjoint: x0 = Pl.H @ x else: x0 = Pr @ x return x0
def build_laguerre_gaussian_source(eps_grid: Grid, omega: float, w0: float, center: np.ndarray, axis: int, slices=List[slice], mu: List[np.ndarray] = None, theta: float = 0, psi: float = 0, polarization_angle: float = 0, m: int = 0, p : int = 0, polarity: int = 1, power: float = 1): """Builds a laguerre gaussian beam source. By default, the laguerre gaussian beam propagates along polarity of the given axis and is linearly polarized along the x-direction if axis is z, y-direction if x and z direction if y. `theta` rotates the propagation direction around the E-field, then 'psi' rotates source plane normal, and the polarization_angle rotates around the propagation direction. Args: eps_grid: gridlock.grid with the permittivity distribution. omega: The frequency of the mode. axis: Direction of propagation. slices: Source slice which define the position of the source in the grid. mu: Permeability distribution. theta: Rotation around the default E-component. psi: Rotation around the source plane normal. polarization_angle: Rotation around the propagation direction. m : parameters of the laguerre gaussian beam p : parameters of the laguerre gaussian beam polarity: 1 if forward propagating. -1 if backward propagating. power: Power is the gaussian beam. Returns: Current source J. """ # Make scalar fields. eps_val = np.real(np.average(eps_grid.grids[0][tuple(slices)])) scalar_field_function = lambda x, y, z, R: laguerre_gaussian_beam_z_axis_x_pol( x, y, z, w0, center, R, omega, m, p, polarity, eps_val) # Get vector fields. fields = scalar2rotated_vector_fields( eps_grid=eps_grid, scalar_field_function=scalar_field_function, mu=mu, omega=omega, axis=axis, slices=slices, theta=theta, psi=psi, polarization_angle=polarization_angle, polarity=polarity, power=power, full_fields=True) # Calculate the source. dxes = [eps_grid.dxyz, eps_grid.autoshifted_dxyz()] # make current field_slices = [slice(None, None)] * 3 J_slices = slices if polarity == -1: ind = slices[axis].start field_slices[axis] = slice(None, ind) J_slices[axis] = slice(ind - 1, ind + 1) else: ind = slices[axis].stop - 1 field_slices[axis] = slice(ind, None) J_slices[axis] = slice(ind - 1, ind + 1) E = np.zeros_like(fields['E']) for i in range(3): E[i][tuple(field_slices)] = fields['E'][i][tuple(field_slices)] full = operators.e_full(omega, dxes, vec(eps_grid.grids), bloch_vec=fields['wavevector']) J_vec = 1 / (-1j * omega) * full @ vec(E) J_temp = unvec(J_vec, E[0].shape) J = np.zeros_like(J_temp) for i in range(3): J[i][tuple(J_slices)] = J_temp[i][tuple(J_slices)] return J, fields['wavevector']
def build_plane_wave_source(eps_grid: Grid, omega: float, axis: int, slices=List[slice], mu: List[np.ndarray] = None, theta: float = 0, psi: float = 0, polarization_angle: float = 0, polarity: int = 1, border: int or List[int] = 0, power: float = 1): """Builds a plane wave source. By default, the plane wave propagates along polarity of the given axis and is linearly polarized along the x-direction if axis is z, y-direction if x and z direction if y. `theta` rotates the propagation direction around the E-field, then 'psi' rotates source plane normal, and the polarization_angle rotates around the propagation direction. Args: eps_grid: gridlock.grid with the permittivity distribution. omega: The frequency of the mode. axis: Direction of propagation. slices: Source slice which define the position of the source in the grid. mu: Permeability distribution. theta: Rotation around the default E-component. psi: Rotation around the source plane normal. polarization_angle: Rotation around the propagation direction. polarity: 1 if forward propagating. -1 if backward propagating. border: border in grid points where the intensity of the planewave decreased to 0. For example: [10, 10, 10] assuming radiation in the z direction will put 10 grid border on both sides in the x and y direction and ignore the z. (If the border is larger then the simulation it will be ignored aswell.) power: power transmitted through the slice. Returns: Current source J. """ # Perpare arguments. shape = eps_grid.shape # Make scalar fields. eps_val = np.real(np.average(eps_grid.grids[0][tuple(slices)])) scalar_field_function = lambda x, y, z, R: plane_wave_z_axis_x_pol( x, y, z, R, omega, polarity, eps_val) # Get vector fields. fields = scalar2rotated_vector_fields( eps_grid=eps_grid, scalar_field_function=scalar_field_function, mu=mu, omega=omega, axis=axis, slices=slices, theta=theta, psi=psi, polarization_angle=polarization_angle, polarity=polarity, power=power, full_fields=True) def make_mask(shape: List[int], slices: List, axis: int, border: List[int]) -> np.ndarray: """Builds a mask with a border. On the border the intensity goes from 1 to 0. Args: shape: Shape of the simulation. slices: List of slices where the plane wave is. axis: Direction of the plane wave. border: number of gridpoint that make up the border. Returns: mask """ size = [s.stop - s.start for s in slices] coords = [] for i in range(3): if (size[i] - 1) > 2 * border[i] and border[i] > 0: d = 1 / np.array(border[i]) x = np.hstack([ np.arange(1, 0, -d), np.zeros(size[i] - 2 * border[i]), np.arange(d, 1 + d, d) ]) else: x = np.zeros(size[i]) coords.append(x) coords[axis] = np.zeros(shape[axis]) ind = np.meshgrid(coords[0], coords[1], coords[2], indexing='ij') co = (ind[0]**2 + ind[1]**2 + ind[2]**2)**0.5 co[co > 1] = 1 mask_slice = 1 + 2 * co**3 - 3 * co**2 mask = np.zeros(shape) slices_mask = copy.deepcopy(slices) slices_mask[axis] = slice(None, None) mask[tuple(slices_mask)] = mask_slice return mask # Prepare border and the mask for be fields. if isinstance(border, int): border = 3 * [border] border[axis] = 0 # You can not have a border in the axis direction size = [s.stop - s.start for s in slices] for i, (b, s) in enumerate(zip(border, size)): if 2 * b < s: border[i] = b elif s == 1: border[i] = 0 else: raise ValueError("Two times the border is larger then the size" + " of the plane wave in axis {}.".format(i)) border = [b if 2 * b < s else 0 for b, s in zip(border, size)] mask = make_mask(shape, slices, axis, border) # Normalize fields dxes = [eps_grid.dxyz, eps_grid.autoshifted_dxyz()] slices_P = copy.deepcopy(slices) slices_P = [ slice(sl.start + b, sl.stop - b) for sl, b in zip(slices_P, border) ] P = waveguide_mode.compute_transmission_chew(E=fields['E'], H=fields['H'], axis=axis, omega=omega, dxes=dxes, slices=slices_P) fields['E'] /= np.sqrt(abs(P)) fields['H'] /= np.sqrt(abs(P)) fields['E'] = [mask * e for e in fields['E']] fields['H'] = [mask * h for h in fields['H']] # Calculate the source. dxes = [eps_grid.dxyz, eps_grid.autoshifted_dxyz()] # make current field_slices = [slice(None, None)] * 3 J_slices = slices if polarity == -1: ind = slices[axis].start field_slices[axis] = slice(None, ind) J_slices[axis] = slice(ind - 1, ind + 1) else: ind = slices[axis].stop - 1 field_slices[axis] = slice(ind, None) J_slices[axis] = slice(ind - 1, ind + 1) E = np.zeros_like(fields['E']) for i in range(3): E[i][tuple(field_slices)] = fields['E'][i][tuple(field_slices)] full = operators.e_full(omega, dxes, vec(eps_grid.grids), bloch_vec=fields['wavevector']) J_vec = 1 / (-1j * omega) * full @ vec(E) J_temp = unvec(J_vec, E[0].shape) J = np.zeros_like(J_temp) for i in range(3): J[i][tuple(J_slices)] = J_temp[i][tuple(J_slices)] return J, fields['wavevector']