emopt.modes

Solve for the modes of electromagnetic waveguides in 2D and 3D.

Waveguide modes can be computed by setting up a generalized eigenvalue problem corresponding to the source-free Maxwell’s equations assuming a solution to \(\mathbf{E}\) and \(\mathbf{H}\) which is proportional to \(e^{i k_z z}\), i.e.

\[ \begin{align}\begin{aligned}\nabla \times e^{i k_z z} \mathbf{E} - i \mu_r \omega e^{i k_z z}\mathbf{H} = 0\\\nabla \times e^{i k_z z} \mathbf{H} + i \epsilon_r \omega e^{i k_z z}\mathbf{E} = 0\end{aligned}\end{align} \]

where we have used the non-dimensionalized Maxwell’s equations. These equations can be written in the form

\[A x = n_z B x\]

where \(A\) contains the discretized curls and material values, \(B\) is singular matrix containing only 1s and 0s, and \(n_z\) is the effective index of the mode whose field components are contained in \(x\). Although formulating the problem like this results in a sparse matrix with ~2x the number of values compared to other formulations discussed in the literature[1], it has the great advantage that the equations remain very simple which simplifies the code. This formulation also makes it almost trivial to implement anisotropic materials (tensors) in the future, if desired.

In addition to solving for the fields of a waveguide’s modes, we can also compute the current sources which excite only that mode. This can be used in conjunction with emopt.fdfd.FDFD to simulated waveguide structures which are particularly interesting for applications in silicon photonics, etc.

References

[1] A. B. Fallahkhair, K. S. Li and T. E. Murphy, “Vector Finite Difference Modesolver for Anisotropic Dielectric Waveguides”, J. Lightwave Technol. 26(11), 1423-1431, (2008).

class emopt.modes.ModeFullVector(wavelength, eps, mu, domain, n0=1.0, neigs=1, backwards=False, verbose=True)

Bases: emopt.modes.ModeSolver

Solve for the modes for a 2D slice of a 3D structure.

Parameters:
  • wavelength (float) – The wavelength of the modes.
  • ds (float) – The grid spacing in the mode field (y) direction.
  • eps (numpy.ndarray) – The array containing the slice of permittivity for which the modes are calculated.
  • mu (numpy.ndarray) – The array containing the slice of permeabilities for which the modes are calculated.
  • n0 (float (optional)) – The ‘guess’ for the effective index around which the modes are computed. In general, this value should be larger than the index of the mode you are looking for. (default = 1.0)
  • neigs (int (optional)) – The number of modes to compute. (default = 1)
  • backwards (bool) – Defines whether or not the mode propagates in the forward +x direction (False) or the backwards -x direction (True). (default = False)
wavelength

The wavelength of the solved modes.

Type:float
neff

The list of solved effective indices

Type:list of floats
n0

The effective index near which modes are found

Type:float
neigs

The number of modes to solve for.

Type:int
build(self)

Build the system of equations and prepare the mode solver for the solution process.

solve(self)

Solve for the modes of the structure.

get_field(self, i, component)

Get the desired raw field component of the i’th mode.

get_field_interp(self, i, component)

Get the desired interpolated field component of the i’th mode

get_mode_number(self, i):

Estimate the number X of the given TE_X mode.

find_mode_index(self, X):

Find the index of a TE_X mode with the desired X.

get_source(self, i, ds1, ds2, ds3=0.0)

Get the source current distribution for the i’th mode.

bc
build()

Build the system of equations and prepare the mode solver for the solution process.

In order to solve for the eigen modes, we must first assemble the relevant matrices \(A\) and \(B\) for the generalized eigenvalue problem given by \(A x = n_x B x\) where \(n_x\) is the eigenvalue and \(x\) is the vector containing the eigen modes.

Notes

This function is run on all nodes.

component_energy(i)

Get the fraction of energy stored in each field component.

Parameters:i (int) – The index of the mode to analyze
Returns:The list of energy fractions corresponding to Ex, Ey, Ez, Hx, Hy, Hz
Return type:[float, float, float, float, float, float]
find_mode_index(P, Q)

Find the index of the index of the mode with X horizontal phase transitions and Y vertical phase transitions.

Parameters:
  • P (int) – The number of horizontal phase transitions
  • Q (int) – The number of vertical phase transitions
Returns:

The index of the mode with the desired number.

Return type:

int

get_field(i, component, permute=True, squeeze=False)

Get the desired raw field component of the i’th mode.

Notes

This function only returns a non-None result on the master node. On all other nodes, None is returned.

See also

ModeFullVector.get_field_interp(), ModeFullVector.find_mode_index()

Parameters:
  • i (int) – The index of the desired mode
  • component (str) – The desired field component (Ex, Ey, Ez, Hx, Hy, Hz)
  • permute (bool) – Permute the field components according to the normal direction of the supplied domain. Because all computation is internally performed assuming the mode propagates in the z direction, the field components need to be permuted in order to produce the desired result. (default = True)
Returns:

(Master node only) an array containing the desired component of the mode field.

Return type:

numpy.ndarray or None

get_field_interp(i, component, squeeze=False)

Get the desired interpolated field component of the i’th mode.

In general, this function should be preferred over ModeFullVector.get_field().

In general, you may wish to solve for more than one mode. In order to get the desired mode, you must specify its index. If you do not know the index but you do know the desired mode number, then ModeFullVector.find_mode_index() may be used to determine the index of the desired mode.

Notes

The fields are solved for on a grid made up of compressed 2D Yee cells. The fields are thus interpolated at the center of this Yee cell (which happens to coincide with the position of Hz)

This function only returns a non-None result on the master node. On all other nodes, None is returned.

See also

ModeFullVector.get_field(), ModeFullVector.find_mode_index()

Parameters:
  • i (int) – The index of the desired mode
  • component (str) – The desired field component (Ex, Ey, Ez, Hx, Hy, Hz)
Returns:

(Master node only) an array containing the desired component of the interpolated mode field.

Return type:

numpy.ndarray or None

get_mode_number(i)

Determine the mode number.

This is based on the number of phase crossings.

Todo

Implement this function…

Parameters:i (int) – The index of the mode to analyze.
Returns:The numbers X and Y of the mode.
Return type:int, int
get_source(i, dx, dy, dz)

Get the source current distribution for the i’th mode.

Notes

  1. Source calculations are only supported for Material3D structures.

2) The sources are computed assuming the mode is propagating in the z direction. In order to support modes propagating in different directions, the spatial coordinates are permuted. Fortunately, the underlying dislocated grids are invariant under this transformation. This is only necessary for calculating the material cross-sections.

Todo

Implement in parallelized manner.

Parameters:
  • i (int) – Index of the mode for which the corresponding current sources are desired.
  • dx (float) – Grid spacing along x direction.
  • dy (float) – Grid spacing along y direction
  • dz (float) – Grid spacing along z direction
Returns:

(On master node) The tuple (Jx, Jy, Jz, Mx, My, Mz) containing arrays of the source distributions.

Return type:

tuple of numpy.ndarray

solve()

Solve for the modes of the structure.

Notes

This function is run on all nodes.

class emopt.modes.ModeSolver(wavelength, n0=1.0, neigs=1)

Bases: future.types.newobject.newobject

A generic interface for electromagnetic mode solvers.

At a minimum, a mode solver must provide functions for solving for the modes of a structure, retrieving the fields of a desired mode, retrieving the effective index of a desired mode, and calculating the current sources which excite that mode.

wavelength

The wavelength of the solved modes.

Type:float
neff

The list of solved effective indices

Type:list of floats
n0

The effective index near which modes are found

Type:float
neigs

The number of modes to solve for.

Type:int
build(self)

Build the system of equations and prepare the mode solver for the solution process.

solve(self)

Solve for the modes of the structure.

get_field(self, i, component)

Get the desired field component of the i’th mode.

get_field_interp(self, i, component)

Get the desired interpolated field component of the i’th mode

get_source(self, i, ds1, ds2, ds3=0.0)

Get the source current distribution for the i’th mode.

build()

Build the system of equations and prepare the mode solver for the solution process.

get_field(i, component)

Get the raw field of the i’th mode.

This function should only be called after solve().

Parameters:
  • i (int) – The number of the desired mode
  • component (str) – The desired field component.
Returns:

(Master node only) The desired field component.

Return type:

numpy.ndarray

get_field_interp(i, component)

Get the interpolated field of the i’th mode.

This function should only be called after solve(). In general, this field should be prefered over get_field().

Parameters:
  • i (int) – The number of the desired mode
  • component (str) – The desired field component.
Returns:

(Master node only) The desired interpolated field component.

Return type:

numpy.ndarray

get_source(i, ds1, ds2, ds3=0.0)

Calculate the current source distribution which will excite the desired mode.

The current source distribution can be computed by assuming the computed mode fields are proportional to \(e^{i k_z z}\) and eminate from a ‘virtual’ plane (hence the fields are zero on one side of plane, and have the desired z-dependence on the other side). This assumed field can be plugged into the source-containing Maxwell’s equations to solve for :math`J` and \(M\).

Parameters:
  • i (int) – The index of the desired mode.
  • ds1 (float) – The grid spacing in the first spatial dimension.
  • ds2 (float) – The grid spacing in the second spatial dimension.
  • ds3 (float) – The grid spacing in the third spatial dimension
neff
solve()

Solve for the fields of the desired modes.

class emopt.modes.ModeTE(wavelength, eps, mu, domain, n0=1.0, neigs=1, backwards=False)

Bases: emopt.modes.ModeSolver

Solve for the TE polarized modes of a 1D slice of a 2D structure.

The TE polarization consists of a non-zeros \(E_z\), \(H_x\), \(H_y\). The mode is assumed to propagate in the x direction and the mode field is a function of the y-position, i.e. the fields are

\[ \begin{align}\begin{aligned}E_z(x,y) = E_{mz}(y) e^{i k_x x}\\H_x(x,y) = H_{mx}(y) e^{i k_x x}\\H_y(x,y) = H_{my}(y) e^{i k_x x}\end{aligned}\end{align} \]

where \(E_{mz}\), \(H_{mx}\), and \(H_{my}\) are the mode fields.

Parameters:
  • wavelength (float) – The wavelength of the modes.
  • ds (float) – The grid spacing in the mode field (y) direction.
  • eps (numpy.ndarray) – The array containing the slice of permittivity for which the modes are calculated.
  • mu (numpy.ndarray) – The array containing the slice of permeabilities for which the modes are calculated.
  • n0 (float (optional)) – The ‘guess’ for the effective index around which the modes are computed. In general, this value should be larger than the index of the mode you are looking for. (default = 1.0)
  • neigs (int (optional)) – The number of modes to compute. (default = 1)
  • backwards (bool) – Defines whether or not the mode propagates in the forward +x direction (False) or the backwards -x direction (True). (default = False)
wavelength

The wavelength of the solved modes.

Type:float
neff

The list of solved effective indices

Type:list of floats
n0

The effective index near which modes are found

Type:float
neigs

The number of modes to solve for.

Type:int
dir

Direction of mode propagation (1 = forward, -1 = backward)

Type:int
bc
The boundary conditions used. The possible boundary conditions are:
0 – Perfect electric conductor (top and bottom) M – Perfect magnetic conductor (top and bottom) E – Electric field symmetry (bottom) and PEC (top) H – Magnetic field symmetry (bottom) and PEC (top) P – Periodicity (top and bottom) EM – Electric field symmetry (bottom) PMC (top) HM – Magnetic field symmetry (bottom) PMC (top)
Type:str
build(self)

Build the system of equations and prepare the mode solver for the solution process.

solve(self)

Solve for the modes of the structure.

get_field(self, i, component)

Get the desired raw field component of the i’th mode.

get_field_interp(self, i, component)

Get the desired interpolated field component of the i’th mode

get_mode_number(self, i):

Estimate the number X of the given TE_X mode.

find_mode_index(self, X):

Find the index of a TE_X mode with the desired X.

get_source(self, i, ds1, ds2, ds3=0.0)

Get the source current distribution for the i’th mode.

bc
build()

Build the system of equations and prepare the mode solver for the solution process.

In order to solve for the eigen modes, we must first assemble the relevant matrices \(A\) and \(B\) for the generalized eigenvalue problem given by \(A x = n_x B x\) where \(n_x\) is the eigenvalue and \(x\) is the vector containing the eigen modes.

Notes

This function is run on all nodes.

dir
find_mode_index(**kwargs)
get_field(**kwargs)
get_field_interp(**kwargs)
get_mode_number(**kwargs)
get_source(i, dx, dy, dz=0.0)

Get the source current distribution for the i’th mode.

Notes

For this calculation to work out, we assume that all field components are zero to the left of the center of the Yee cell (i.e. the positions of the Ez values). To the right of the center of the Yee cell, we assume the field components have an exp(ikx) dependence.

dy should be equal to ds.

This class assumes all modes propagate in the x direction. In order to propagate a mode in the y direction, x and y (dx and dy) can be permuted.

Todo

Implement in parallelized manner.

Parameters:
  • i (int) – Index of the mode for which the corresponding current sources are desired.
  • dx (float) – The grid spacing in the x direction.
  • dy (float) – The grid spacing in the y direction.
  • dz (float) – Unused in ModeTE
Returns:

(On ALL nodes) The tuple (Jz, Mx, My) containing arrays of the source distributions. In 2D, these source distributions are N x 1 arrays.

Return type:

tuple of numpy.ndarray

solve()

Solve for the modes of the structure.

In addition to solving for the modes, this function saves the results to the master node so that they can be easily retrieved for visualization, etc.

Notes

This function is run on all nodes.

class emopt.modes.ModeTM(wavelength, eps, mu, domain, n0=1.0, neigs=1, backwards=False)

Bases: emopt.modes.ModeTE

Solve for the TM polarized modes of a 1D slice of a 2D structure.

The TM polarization consists of a non-zeros \(H_z\), \(E_x\), \(E_y\). The mode is assumed to propagate in the x direction and the mode field is a function of the y-position, i.e. the fields are

\[ \begin{align}\begin{aligned}H_z(x,y) = H_{mz}(y) e^{i k_x x}\\E_x(x,y) = E_{mx}(y) e^{i k_x x}\\E_y(x,y) = E_{my}(y) e^{i k_x x}\end{aligned}\end{align} \]

where \(H_{mz}\), \(E_{mx}\), and \(E_{my}\) are the mode fields.

Parameters:
  • wavelength (float) – The wavelength of the modes.
  • ds (float) – The grid spacing in the mode field (y) direction.
  • eps (numpy.ndarray) – The array containing the slice of permittivity for which the modes are calculated.
  • mu (numpy.ndarray) – The array containing the slice of permeabilities for which the modes are calculated.
  • n0 (float (optional)) – The ‘guess’ for the effective index around which the modes are computed. In general, this value should be larger than the index of the mode you are looking for. (default = 1.0)
  • neigs (int (optional)) – The number of modes to compute. (default = 1)
  • backwards (bool) – Defines whether or not the mode propagates in the forward +x direction (False) or the backwards -x direction (True). (default = False)
wavelength

The wavelength of the solved modes.

Type:float
neff

The list of solved effective indices

Type:list of floats
n0

The effective index near which modes are found

Type:float
neigs

The number of modes to solve for.

Type:int
build(self)

Build the system of equations and prepare the mode solver for the solution process.

solve(self)

Solve for the modes of the structure.

get_field(self, i, component)

Get the desired raw field component of the i’th mode.

get_field_interp(self, i, component)

Get the desired interpolated field component of the i’th mode

get_mode_number(self, i):

Estimate the number X of the given TE_X mode.

find_mode_index(self, X):

Find the index of a TE_X mode with the desired X.

get_source(self, i, ds1, ds2, ds3=0.0)

Get the source current distribution for the i’th mode.

bc
get_field(**kwargs)
get_field_interp(**kwargs)
get_source(dx, dy, dz=0.0)

Get the source current distribution for the i’th mode.

Notes

For this calculation to work out, we assume that all field components are zero to the left of the center of the Yee cell (i.e. the positions of the Hz values). To the right of the center of the Yee cell, we assume the field components have an exp(ikx) dependence.

dy should be equal to ds.

This class assumes all modes propagate in the x direction. In order to propagate a mode in the y direction, x and y (dx and dy) can be permuted.

Todo

Implement in parallelized manner.

Parameters:
  • i (int) – Index of the mode for which the corresponding current sources are desired.
  • dx (float) – The grid spacing in the x direction.
  • dy (float) – The grid spacing in the y direction.
  • dz (float) – Unused in ModeTE
Returns:

(On ALL nodes) The tuple (Mz, Jx, Jy) containing arrays of the source distributions. In 2D, these source distributions are N x 1 arrays.

Return type:

tuple of numpy.ndarray