Low-level (private) API: Classes and functions intended for use within meep_adjoint

TimeStepper: Interface to computational back-end provided by meep or other FDTD solver

class TimeStepper(obj_func, dft_cells, basis, sim, fwd_sources)[source]

Bases: object

Abstraction of low-level FDTD engine.

TimeStepper is a class that knows how to invoke an FDTD solver to execute a time-domain calculation to obtain frequency-domain (DFT) field components in objective regions, from which may be computed (a) the values of objective quantities and objective functions, or (b) permittivity derivatives of the objective function, from which may be computed the objective-function gradient.

This class lies between the top-level OptimizationProblem session-manager class and lower-level classes like ObjectiveFunction. It exports a single method (__call__) which prepares and executes one complete FDTD timestepping run to compute frequency-domain fields, returning numerical quantities of interest computed from these fields. __call__ accepts one str-valued argument job, for which the recognized values are forward or adjoint. For job`==`forward, the excitation sources for the FDTD problem are the user-defined sources passed to the OptimizationProblem constructor and the result of the calculation is the objective-function value (plus the values of any additional requested objective quantities). For job`==`adjoint, the excitation sources are automatically determined internally and the result of the calculation is the objective-function gradient.

TimeStepper automatically determines when to terminate a timestepping run by monitoring the numerical convergence of the output quantities. The details of this process may be customized using configuration options. TimeStepper does not itself know how to evaluate its output quantities, but rather outsources these calculations to public methods of ObjectiveFunction and other low-level classes.

__call__(job):

Populate the FDTD grid with an appropriate source distribution, initialize DFT cells for tabulating frequency-domain fields, then execute FDTD timestepping until the output quantities converges and return those quantities.

Constructor.

Parameters
  • obj_func (ObjectiveFunction) – The function whose value or gradient we compute (copied from OptimizationProblem)

  • dft_cells (list of DFTCell”) – List of DFTCell structures, assumed to be ordered with objective cells first and the design cell last (so any extra cells are in the interior of the list).

  • basis (Basis) – Function space (copied from OptimizationProblem). Logically this is not a necessary input for this calculation—it is used only in a post-processing step to compute the objective-function gradient by projecting df/dEps onto the function space. It might make more sense to define the output of TimeStepper to be the raw df/dEps matrix, with the projection step done at a higher level, in which case this parameter could be omitted. But then the convergence of the adjoint timestepping run would have to consider the full raw df/dEps matrix instead of the lower-dimensional gradient vector.

  • sim (meep.Simulation) – Simulation object, copied from OptimizationProblem.

  • fwd_sources (list of meep.Source) – User-specified sources for the forward calculation.

run(job)[source]

Execute a forward or adjoint FDTD timestepping run and return results.

Parameters

job (str) – ‘forward’ or ‘adjoint’

prepare(job='forward')[source]

Prepare simulation for timestepping by adding sources and DFT cells.

Parameters

(str) (job) –

  1. If job==’forward’, prepare forward run to compute objective function value.

  2. If job==’adjoint’, prepare adjoint run to compute objective function gradient.

  3. If job is the name of an objective quantity, prepare adjoint run to compute the gradient of that quantity.

get_adjoint_sources(qname=None)[source]

Construct adjoint source distribution.

Return a list of mp.Source structures describing the distribution of sources appropriate for adjoint-method evaluation of the objective-function gradient df/deps.

The optional parameter qname may be set to the name of an objective quantity Q, in which case we instead compute dQ/deps.

ObjectiveFunction: Evaluation of objective-function values and partial derivatives

class ObjectiveFunction(fstr='S_0', extra_quantities=[])[source]

Bases: object

Multivariate scalar-valued function defined by string expression.

An ObjectiveFunction is a scalar function \(f^{obj}({q_n})\) of \(N\) scalar variables \(\{ q_0, q_1, ..., q_{N-1}\}\), where the \({q_n}\) are complex-valued in general and \(f^{obj}\) may be real- or complex-valued (only the real part is referenced for optimization purposes).

An instance of ObjectiveFunction is specified by a character string (the fstr input to the constructor) that, upon parsing by sympy.sympify, is identified as a mathematical expression depending on \(N\) unknown variables (which we call “objective quantities”). The strings identified by sympy as the names of the variables should obey meep_adjoint naming conventions for objective quantities (in particular, they should be parsable by make_qrule to yield a valid qrule).

Parameters
  • fstr (str) – Mathematical expression defining objective function.

  • extra_quantities (list of str, optional) – Optional list of additional objective quantities to be computed and returned each time the objective function is evaluated.

Class instances store the following data:

fexpr (sympy expression)

sympy expression returned by sympy.sympify(fstr)

qsyms (list of sympy.Symbol)

The variable (unknown) quantities on which the objective function depends, as identified by sympy upon parsing fstr.

qnames (list of str)

names of objective quantities

qrules (list of QRule)

rules specifying how the objective quantities are to be computed from FDTD data (specifically, from frequency- domain field data stored in DFTCells)

qvals (array-like)

cache storing most recently computed values of objective quantities

riqsyms, riqvals

as qsyms and qvalues, but with each complex-valued ‘q’ quantity split up into real-valued ‘r’ and ‘i’ components. We do this to facilitate symbolic differentiation of non-analytic functions of objective quantities such as \(|q_i|^2\)—which, incidentally, would be written within fstr like this: ‘Abs(q_i)**2’

get_dfdq()[source]

Compute first partial derivatives of objective function.

Compute the partial derivatives \(\partial f / \partial q_n\) using values of \(\{q_n\}\) cached on most recent invocation of __call__ (which must have been invoked at least once before this function can be called).

Returns

Array whose *n*th entry is \(\partial f/\partial q_n\).

Return type

array-like

DFTCell: Storage for frequency-domain field components and evaluation of objective quantities

class DFTCell(region, components=None, fcen=None, df=None, nfreq=None)[source]

Bases: object

Simpler data structure for frequency-domain fields in MEEP

The instantiating data of a DFTCell are a grid subregion, a set of field components, and a list of frequencies. These metadata fields remain constant throughout the lifetime of a DFTCell. In addition to the metadata, instances of DFTCell allocate internal arrays of DFT registers in which the specified components of the frequency-domain fields at the specified grid points and frequencies are accumulated over the course of a MEEP timestepping run. These registers are reset to zero at the beginning of the next timestepping run, but in the meantime you can use the save_fields method to save an internally cached snapshot of the fields computed on a given timestepping run.

Note: for now, all arrays are stored in memory. For large calculations with many DFT frequencies it might make sense to implement a disk-caching scheme.

The internally-stored frequency-domain fields at a single frequency may be fetched via the get_EH_slice (single component) or get_EH_slices (all components) methods. By default these routines return slices of the currently active fields (i.e. the fields computed on the most recent timestepping run), but they accept an optional parameter specifying the label of a data set stored by a call to save_fields after a previous timestepping run.

For convenience, DFTCell also offers a get_eigenmode_slices() method that computes and returns eigenfield profiles in the same format as DFT fields.

DFTCells also know how to crunch their internally-stored field-component data to compute various physical quantities of interest, such as Poynting fluxes and field energies. We consider this the primary functionality exported by DFTCell, and thus implement it as the __call__ method of the class.

DFTCell replaces the DftFlux, DftFields, DftNear2Far, DftForce, DftLdos, and DftObj structures in core pymeep.

Parameters
  • region (Subregion) – Subregion of computational cell in which to compute frequency-domain field components.

  • components (list of meep components (i.e. [mp.Ex, mp.Hy]), optional) – Field components to compute.

  • df, nfreq (fcen,) – Set of frequencies at which to compute FD fields.

register(sim)[source]

‘Register’ the cell in a MEEP simulation to request computation of frequency-domain fields.

Parameters

sim (mp.Simulation) –

get_EH_slice(c, nf=0)[source]

Fetch array of frequency-domain amplitudes for a single field component.

Compute an array of frequency-domain field amplitudes, i.e. a frequency-domain array slice, for a single field component at a single frequency in the current simulation. This is like mp.get_dft_array(), but ‘zero-padded:’ when the low-level DFT object does not have data for the requested component (perhaps because it vanishes identically by symmetry), this routine returns an array of the expected dimensions with all zero entries, instead of a rank-0 array that prints out as a single preposterously large or small floating-point number,

which is the not-very-user-friendly behavior of mp.get_dft_array().

Parameters
  • c ((mp.component)) – Field component to fetch.

  • nf (int, optional) – Frequency index, by default 0

Returns

Complex-valued array giving field amplitude at grid points.

Return type

np.array

get_EH_slices(label=None, nf=0)[source]

Fetch arrays of frequency-domain field amplitudes for all stored components.

Return a 1D array (list) of arrays of frequency-domain field amplitudes, one for each component in this DFTCell, at a single frequency in a single MEEP simulation. The simulation in question may be the present, ongoing simulation (if label==None), in which case the array slices are read directly from the currently active meep DFT object; or it may be a previous simulation (identified by label) for which DFTCell::save_fields(label) was called at the end of timestepping.

Parameters
  • label ([str], optional) – Label of saved data to retrieve.

  • nf (int, optional) – Frequency index, by default 0

Returns

Arrays of field-component amplitudes at grid points

Return type

list of np.array

Raises

ValueError – if no data exists for the specified label.

subtract_incident_fields(EHT, nf=0)[source]

Substract incident from total fields to yield scattered fields.

Parameters
  • EHT (list of arrays of field component amplitudes, as) – returned by get_EH_slices, for the total fields

  • nf (int, optional) – frequency index, by default 0

save_fields(label)[source]

Save current values of grid fields internally for later use.

This routine tells the DFTCell to create and save an archive of the frequency-domain array slices for the present simulation—i.e. to copy the frequency-domain field data out of the sim.dft_obj structure and into an appropriate data buffer in the DFTCell, before the sim.dft_obj data vanish when sim is reset for the next run. This routine should be called after timestepping is complete. The given label is used to identify the stored data for future retrieval.

Parameters

label (str) – Label assigned to data set, used subsequently for retrieval.

get_eigenmode_slices(mode, nf=0)[source]

Like get_EH_slices, but for eigenmode fields.

Return a 1D array (list) of arrays of field amplitudes for all tangential E,H components at a single frequency—just like get_EH_slices()—except that the sliced E and H fields are the fields of eigenmode #mode.

Parameters
  • mode (int) – Eigenmode index.

  • nf (int, optional) – Frequency index, by default 0

Returns

Return type

list of np.arrays (same format as returned by get_EH_slices)

Subregion: Rationalized, unified handling of spatial subregions

class Subregion(xmin=None, xmax=None, center=array([0., 0., 0.]), size=None, normal=None, dir=None, name=None)[source]

Bases: object

Subregion of computational cell.

A Subregion is a finite hyperrectangular region of space contained within the extents of the FDTD computational grid.

A Subregion may be of codimension 1 (i.e. it is a line in 2D or a plane in 3D), in which case it has a normal direction. Codim-1 subregions are used to define eigenmode sources and to evaluate Poynting fluxes and eigenmode expansion coefficients.

Alternatively, the subregion may be of codimension 0 [i.e. it is a full rectangle (2D) or box (3D)], or of dimension 0 [i.e. it is a point]. The normal direction is undefined in these cases.

A Subregion has a name (str), which is user-assigned or autogenerated if left unspecified.

Note

Subregion is a common replacement for FluxRegion, EnergyRegion, and the other similar data structures in libmeep. Its advantages are that (a) it eliminates redundant code and cumbersome API conventions imposed by the coexistence of multiple distinct data structures all describing the same thing, and that (b) it adds a user-assignable name field, which would be convenient to have already in core pymeep but is essential in meep_adjoint to allow objective quantities to be assigned names that identify the objective region for which they are defined.

Parameters
  • xmin (array-like, optional) – minimal corner, by default None

  • xmax (array-like, optional) – maximal corner, by default None

  • center (array-like, optional) – center, by default ORIGIN

  • size (array-like, optional) – size, by default None

  • normal (array-like, optional) – normal direction for codim-1 regions, by default None

  • name (str, optional) – arbitrary caller-chosen label; autogenerated if not specified