meep_adjoint: A python package for adjoint sensitivity analysis in MEEP

This is the root of the documentation tree for meep_adjoint. Jump directly to the package-wide Table of Contents below, or read on for a quick-start summary.

What does this package do?

You’ll find a longer answer in the Overview (and a more succinct one in the API Reference), but, in a nutshell: It extends the computational capabilities of the core meep solver in a particular way that facilitates interaction with numerical optimization algorithms, opening the door to intelligent design tools that automatically design devices to meet given performance specifications.

Wait, what exactly does the package do again?

If that was a bit vague, we can be more specific about precisely what meep_adjoint does. Consider a typical design problem in which we seek to tune a device geometry to optimize some performance metric. For example, in the right-angle router example in the meep_adjoint example gallery, we are designing a four-waveguide interconnect for an optical network, and our goal is to choose the permittivity distribution \(\epsilon(\mathbf{x})\) in the junction region to steer incoming optical signals arriving on the West port around a 90-degree bend to the North port, ideally with zero leakage power emitted from the South and East ports:

Mathematical formulation of design-optimization problems

To formulate this problem mathematically so we can hand it off to a numerical optimizer, we might begin by expressing the unknown design function as an expansion in some convenient finite set of basis functions:

\[\epsilon(\mathbf x)\approx\sum_{d=1}^D \beta_d b_d(\mathbf x)\]

Then each possible design configuration corresponds to a \(D\)-dimensional vector of coefficient values \(\boldsymbol{\beta}=\{\beta_1,\cdots,\beta_D\}\), while the metric defining the performance of a design—the quantity we are trying to optimize—is a (real-valued, scalar) function of a vector-valued argument, the objective function \(f^\text{obj}(\boldsymbol{\beta}).\) For the right-angle router with fixed input power entering the West port, we could define our objective to be simply to maximize output power from the North port, i.e.

\[f^\text{obj}(\boldsymbol{\beta}) = S_\text{North} \tag{1}\]

where \(S_\text{North}\) denotes the outgoing power flux (integral of normal Poynting vector) through the North flux monitor. Alternatively, assuming the input power is delivered in its entirety by a single mode—call it mode \(m\)— of the West waveguide (as would be the case if we excited our FDTD simulations with an EigenmodeSource tuned to mode \(m\)), we could optimize for maximal output power carried by mode \(m\) of the North waveguide, or equivalently for maximal magnitude of the forward-traveling b`mode-expansion coefficient`_ evaluated at the North flux monitor, in which case we would instead have

\[f^\text{obj}(\boldsymbol{\beta}) = |P^m_\text{North}|^2 \tag{2}\]

where :py:`P^m_\text{North}` denotes the coefficient of the math:$m$th forward-traveling waveguide mode at the North flux monitor. (The use of symbols \(S\) and \(P^m\) respectively for Poynting flux and forward-traveling mode-expansion coefficient is part of the meep_adjoint rules for labeling objective quantities.)

Mathematical formulation of design-optimization problems

For our in the router problem a real-valued scalar device given by a and we can picture the process of device optimization as a journey through a \(D\)-dimensional space in search of the magical point \(\boldsymbol{\beta}_*\) at which single point moving through a location. To the performance of a location. To the performance of a

For a

or equivalently to a \(D\)-dimensional vector \({\boldsymbol{\beta}}\), ranges values of the \(D\) coefficients evice design is specified by the \(D\)-dimensional vector of numbers \(\boldsymbol{\beta}\)

sum_{d=1}^D beta_d b_d(mathbf x)

Then, for given fixed values of the input power, operating frequency, and other parameters, the design metric we are trying to optimize—that is, our objective function \(f^\text{obj}\)—may be thought of as a function of the \(D\)-dimensional vector of expansion coefficients \(\boldsymbol{\beta}\), i.e.

\[ \begin{align}\begin{aligned} f^\text{obj}(\boldsymbol{\beta}) =\text{power output from \textbf{North} port} \equiv S_{\text{North}}\\ There's just one problem, which perhaps already occurred to you if you have any experience with high-dimensional optimization:\\and asks for the optimal\end{aligned}\end{align} \]

Then, for given fixed values of the input power, operating frequency, and other parameters, the ia the West input waveguide port

^text{design}(mathbf x)approx

sum_{d=1}^D beta_d b_d(mathbf x)

Then, for given fixed values of the input power, operating frequency, and other parameters, the ^text{design}(mathbf x)approx sum_{d=1}^D beta_d b_d(mathbf x)

Then, for given fixed values of the input power, operating frequency, and other parameters, the ia the West input waveguide port to the North output wav

Here \(\epsilon(x)\)

\[\epsilon(\mathbf x)=\sum \beta_d b_d(\mathbf x)\]

Now suppose some region of the material geometry is free to be tweaked

you’re a device designer tasked with maximizing some performance metric $f$ expressed as a function of these output quantities—for example, we might put \(f=S_2(\omega_3)\) to maximize flux through the 2nda flux region at the 3rd frequency, or \(f = (S_1(\omega_3) - S_2(\omega_3))^2\) to maximize the difference between two fluxes, etc.

The scope of meep_adjoint

Q. Does this mean that the package only computes objective-function gradients? That is, it doesn’t actually do any optimization?

A. The primary mission of meep_adjoint is to compute objective-function gradients. This is the task the package guarantees to execute efficiently and accurately,

and it’s one that’s self-contained, unambiguous, and easily testable. 1

The larger question of how best to use gradient information for design automation—which involves questions such as which of the myriad available gradient-based optimization algorithms to use, and how to configure its tunable parameters—is officially beyond the purview of meep_adjoint, and indeed is much too broad a problem to be treated with anything approaching comprehensiveness by any single package. We hope meep_adjoint will be helpful to meep users as they navigate this vast domain.

With all of that by way of disclaimer, however, we note that meep_adjoint does ship with one (rather simple-minded) implementation of an optimization algorithm—namely, a basic gradient-descent solver. Per the discussion above, we make no claim as to the robustness or efficiency of this solver, and we encourage users to consider it a first step in the process of optimizing any geometry, to be replaced by more sophisticated solvers as necessary; but, having said that, we note that the simple built-in optimizer suffices to yield good results on all the problems considered in the example gallery.

What does a typical workflow look like?

You’ll find a full step-by-step walkthrough in the Tutorial and additional guided case studies in the Example gallery, but here is a quick rundown:

1) Initialization and Problem Definition: You begin by creating an instance of optimization_problem This is the top-level python class exported by meep_adjoint, analogous to the Simulation class in the core meep python module ; it stores all data and state relevant to the progress of a design optimization, and you will access most meep_adjoint functionality via its class methods.

2) Interactive single-point calculations Before launching a full-blown iterative optimization run that could run for hours or days, you will probably want to run some sanity-check calculations involving your geometry. These include…**(section incomplete)**

3) Full iterative optimization: Launch your optimization run and monitor its progress via graphical or other indicators.

How is the documentation structured?

Table of Contents

Indices and tables

..###################################################################### ..###################################################################### ..######################################################################

1

For example, one obvious test of the correctness of meep_adjoint is to estimate objective-function derivatives by numerical finite-differencing and compare to components of the adjoint-method gradient. This is the basis of one of the tests in the meep_adjoint unit-test suite, and also of the holey waveguide example in the example gallery.