Inverse design using lumopt can be run from the CAD script editor, the command line or any python IDE. In the first sub-section, we will briefly describe how to import the lumopt and lumapi modules; although an experienced python user can likely skip this part. As a starting point, it is recommended to run the AppGallery examples and use these as templates for your own project. It may be helpful following along with these files as you read this page or you may simply reference this page when running the examples later. In the Project Init section, we outline the project inputs and necessary simulation objects that should be included. Important lumopt specific considerations are highlighted; however, a valid simulation set-up is imperative, so convergence testing should be considered a pre-requisite. Next important lumopt set-up classes, that should be updated to reflect your specifications are documented. Finally, a description of the scipy optimizer and lumopt optimization classes are presented. Shape and topology optimization primarily differ in how they handle the optimizable geometry which is the subject of the next page in this series.
Please note that the version of lumopt shipped with the Lumerical products, documented in this Knowledge Base, differs from the initial efforts undertaken by Christopher Keraly, which is documented using Lumopt Read the Docs. Even though there are many commonalities, in these documentations, we exclusively refer to the bundled Lumerical version of lumopt.
Install
Running from the CAD script editor has the advantage that it requires no set-up and uses a Python 3 distribution that ships with the Lumerical installer, so there is no need to install Python separately. This method automatically configures the workspace to find the lumopt and lumapi modules and would be the preferred method for users with little experience in Python.
Using your own version of Python and running from an IDE or the command line may be preferable for more experienced users. To do this one simply needs to import lumopt and lumapi which will require specifying the correct path. Either pass an explicit path using importlibutil, or updating the search path permanently appending the PythonPath object. Advanced user working with numerous libraries might want to create a Lumerical virtual environment. For more information on these methods and os specific paths, see Session management - Python API.
Note Lumerical ships with a version of Python 3, including lumapi and lumopt modules, already installed. To run any of our examples 'out of the box' simply run the scripts from the script file editor in the CAD. |
Project Init
Base Simulation
The base simulation needs to be defined using one of the following options
- Predefined simulation file - Initialize a python variable that specifies the path to the base file. Example Grating coupler.
base_sim = os.path.join(os.path.dirname(__file__), 'grating_base.fsp')
- An lsf set-up script - Create a python variable using the load_from_lsf function. Example Waveguide crossing.
from lumopt.utilities.load_lumerical_scripts import load_from_lsf
crossing_base = load_from_lsf('varFDTD_crossing.lsf')
- Callable python code that does the set-up using the API - This can be a function defined in the same file or an imported function. Example Y-branch.
sys.path.append(os.path.dirname(__file__)) #Add current directory to Python path
from varFDTD_y_branch import y_branch_init_ #Import y_branch_init function from file
y_branch_base = y_branch_init_ #New handle for function
Each method produces a file which the optimizer updates and runs. Since the resulting project files should be equivalent; the method each user employs is a matter of preference or convenience.
Required Objects
In the varFDTD, and FDTD base simulation it is also necessary to provide input/output geometry and define the following simulation objects that are used by lumopt.
There are currently two figures of merit supported by lumopt, ModeMatch, which captures the power coupling of guided modes, and IntensityVolume, which captures the light intensity over a surface or volume for a specified wavelength. Depending on the figure of merit (FOM) used, the required objects for the base simulation are different.
The table below indicates what is required for each figure of merit. Further requirements with regards to the objects for each FOM are written below the table.
Figure of Merit | Required Objects |
ModeMatch |
|
IntensityVolume |
|
ModeMatch figure of merit – Simulation object notes
A screenshot of the necessary objects for the ModeMatch figure of merit is shown below. The objects are labeled and shown in the yellow box.
The mode source should have a large enough span and the modes should be compared to expectations see FDE convergence. This is used as the forward source. A mesh override is placed over the optimization region to ensure that a fine uniform grid covers this space, and the opt_field monitor is used to extract the fields in this region. The FOM monitor should be aligned to the interface of a mesh cell to avoid interpolation errors; therefore, it is a good idea to have a mesh override co-located with the FOM monitor. In the adjoint simulation, the adjoint source will take the place of the FOM monitor. Passing the name of the FOM monitor, to modematch class, allows multiple FOM monitors to be defined in the same base file which is helpful for SuperOptimization.
IntensityVolume figure of merit – Simulation object notes
A screenshot of the necessary objects for the IntensityVolume figure of merit is shown below. The objects are labeled and shown in the gold box.
For more information on object settings, please refer to the example file in the Application Gallery example on the Inverse Design of Metasurface Color Router.
Set-up Classes
Two important lumopt classes that should be updated with your parameters are wavelengths and figure of merit classes. These are used to define the spectrum and mode number(or polarization) of the FOM respectively.
We currently accept two figures of merit, power coupling of guided modes, and intensity of light over a surface or volume. Other figures of merit, such as optimizing the target phase or power to a specified grating order are not supported.
To compute the broadband figure of merit for ModeMatch, we take the mean of the target minus the error using the p-norm as follows.
$$F=\left(\frac{1}{\lambda_{2}-\lambda_{1}} \int_{\lambda_{1}}^{\lambda_{2}}\left|T_{0}(\lambda)\right|^{p} d \lambda\right)^{1 / p}-\left(\frac{1}{\lambda_{2}-\lambda_{1}} \int_{\lambda_{1}}^{\lambda_{2}}\left|T(\lambda)-T_{0}(\lambda)\right|^{p} d \lambda\right)^{1 / p}$$
where
- \( T_{0} \) is the target_T_fwd
- \( \lambda_{1} \text{ and } \lambda_{2} \) are the lower and upper limits of the wavelength points
- \( T \) is the actual mode expansion power transmission
- \( p \) is the value of the generalized p-norm
To compute the figure of merit for IntensityVolume, the following formula is used, where square of the magnitude of each component of the electric field is summed over the region specified by the monitor.
$$F = \sum_{i=0}^N \left( |E_x (\vec{r}_i, \lambda_0)|^2 + |E_y (\vec{r}_i, \lambda_0)|^2 + |E_z (\vec{r}_i, \lambda_0)|^2 \right)$$
where
- \(\vec{r}_i\) is the spatial position of a cell inside the region specified by the monitor
- \(E\) is the electric field
- \(\lambda_0\) is the specified wavelength to be optimized
Wavelengths
Defines the simulation bandwidth, and wavelength resolution. The target FOM spectrum for optimization of the power coupling of guided modes is specified in the ModeMatch object.
from lumopt.utilities.wavelengths import Wavelengths
class Wavelengths(start,
stop,
points)
: start: float
Shortest wavelength [m]
: stop: float
Longest wavelength [m]
: points: int
The number of points, uniformly spaced including the endpoints.
Example
wavelengths = Wavelengths(start = 1260e-9, stop = 1360e-9, points = 11)
ModeMatch
This class is used to define target mode, propagation direction, and specify the broadband power coupling.
from lumopt.figures_of_merit.modematch import ModeMatch
class ModeMatch(monitor_name,
mode_number,
direction,
target_T_fwd,
norm_p,
target_fom)
: monitor_name: str
Name of the FOM monitor in the file.
: mode_number : str or int
Used to specify the mode.
If the varFDTD solver is used:
- ‘fundamental mode’
- int - user select mode number
If the FDTD solver is used:
- 'fundamental mode'
- 'fundamental TE mode'
- 'fundamental TM mode'
- int - user select mode number
: direction : str
The direction is determined by the FDTD coordinates; for mode traveling in the positive direction the direction is forward.
- 'Backward'
- 'Forward'
: multi_freq_source: boolean, optional
Should only be enabled by advanced users. See frequency Frequency dependent mode profile for more info. Default = False
: target_T_fwd: float or function
A function which will take the number of Wavelengths points and return values [0,1]. Usually passed as a lambda function or a single float value for single wavelength FOM. To specify a more advanced spectrum one can define a function, it may be helpful to use, numpy windows as a template.
: norm_p: float
Is the generalized p-norm used in the FOM calculation. The p-norm, with \( p \geq 1 \) allows the user to increase the weight of the error. Since \( p=1 \) provides a lower bound on this function, a higher p-number will increase the weight of the error term. Default p =1.0
: target_fom: float
A target value for the figure of merit. This will change the behavior of the printing and plotting only. If this is enabled, by setting a value other than 0.0, the distance of the current FOM is given. Default = 0.0
Example
class ModeMatch(monitor_name = 'fom', mode_number = 3, direction = 'Backward', target_T_fwd = lambda wl: np.ones(wl.size), norm_p = 1)
IntensityVolume
This class is used to define the target volume or surface to be used for optimization of light intensity for a specific wavelength.
Note: If a range of wavelength/frequency are specified in the field region object, the center wavelength/frequency will be used.
from lumopt.figures_of_merit.intensity_volume import IntensityVolume
class IntensityVolume(field_region_name)
:field_region_name: str
Name of the FOM Field Region Object name.
Optimization Classes
Here we describe the generic ScipyOptimizer wrapper, and lumopt Optimization class which is used to encapsulate the project.
ScipyOptimizer
This is a wrapper for the generic and powerful SciPy optimization package.
from lumopt.optimizers.generic_optimizers import ScipyOptimizers
Class ScipyOptimizers(max_iter,
method,
scaling_factor,
pgtol,
ftol,
scale_initial_gradient_to,
penalty_fun,
penalty_jac)
: max_iter: int
Maximum number of iterations; each iteration can make multiple figure of merit and gradient evaluations. Default = 100
: method: str
Chosen minimization algorithm; experimenting with this option should only be done by advanced users. Default = ‘L-BFGS-B'
: scaling_factor: none, float, np.array
None, scalar or a vector the same length as the optimization parameters. This is used to scale the optimization parameters. As of 2021R1.1, the default behavior in shape optimization is to automatically map the parameters the range [0,1] within the optimization routines; which was always the case in topology. The bounds, defined in the geometry class, or eps_min/eps_max are used for this. Default = None
: pgtol: float
The iteration will stop when \( \max( |\text{proj }g_i | \text{ i = 1, ..., n} ) <= pgtol| \) where \( g_i \) is the i-th component of the projected gradient. Default = 1.0e-5
: ftol: float
The iteration stops when \( \left(( f^k - f^{k+1}) / \max (| f^k |\text{ , }|f^{k+1}|\text{ , }1 ) \right) <=ftol \). Default = 1.0e-5
: scale_initial_gradient_to: float
Enforces a rescaling of the gradient to change the optimization parameters by at least this much; the default value of zero disables automatic scaling. Default = 0.0
: penalty_fun: function, optional
Penalty function to be added to the figure of merit; it must be a function that takes a vector with the optimization parameters and returns a single value. Advanced feature. Default = None
:penalty_jac: function, optional
The gradient of the penalty function; must be a function that takes a vector with the optimization parameters and returns a vector of the same length. If a penalty_fun is included with no penalty_jac, lumopt will approximate the derivative. Advanced feature. Default = None
Example
optimizer = ScipyOptimizers(max_iter = 200,
method = 'L-BFGS-B',
scaling_factor = 1.0,
pgtol = 1.0e-5,
ftol = 1.0e-5,
scale_initial_gradient_to = 0.0,
penalty_fun = penalty_fun,
penalty_jac = None)
Optimization
Encapuslates and orchestrates all of the optimization pieces, and routines. Calling the opt.run method will perform the optimization.
from lumopt.optimization import Optimization
class Optimization(base_script,
wavelengths,
fom,
geometry,
optimizer,
use_var_fdtd,
hide_fdtd_cad,
use_deps,
plot_history,
store_all_simulations,
save_global_index,
label,
source_name)
: base_script: callable, or str
Base simulation - See project init.
- Python function in the workspace
- String that points to base file
- Variable that loads from lsf script
: wavelengths: float or class Wavelengths
Provides the optimization bandwidth. Float value for single wavelength optimization and Wavelengths class provides a broadband spectral range for all simulations.
: fom: class ModeMatch or class IntensityVolume
The figure of merit FOM, see ModeMatch and IntensityVolume
: geometry: Lumopt geometry class
This defines the optimizable geometry, see Optimizeable Geometry
: optimizer: class ScipyOptimizers
See ScipyOptimizer for more information.
: hide_fdtd_cad: bool
Flag to run FDTD CAD in the background. Default = False
: use_deps: bool
Flag to use the numerical derivatives calculated directly from FDTD. Default = True
: plot_history: bool
Plot the history of all parameters (and gradients). Default = True
: store_all_simulations: bool
Indicates if the project file for each iteration should be stored or not. Default = True
: save_global_index: bool
Flag to save the results from a global index monitor to file after each iteration (used for visualization purposes). Default = False
: label: str, optional
If the optimization is part of a super-optimization, this string is used for the legend of the corresponding FOM plot. Default = None
: source_name: str, optional
Name of the source object in the simulation project. Default = "source"
Example
opt_2d = Optimization(base_script = base_sim_2d,
wavelengths = wavelengths,
fom = fom,
geometry = geometry,
optimizer = optimizer,
use_var_fdtd = False,
hide_fdtd_cad = True,
use_deps = True,
plot_history = True,
store_all_simulations = True,
save_global_index = False,
label = None)
Note(Advanced): For 2020R2 we exposed, a prototype user-requested debugging function that allows the user to perform checks of the gradient calculation. This is a method of the optimization class and can be called as follows.
opt.check_gradient(intitial_guess, dx=1e-3)
Where initial_guess is a numpy array that specifies the optimization parameters at which the gradient should be checked. The scalar parameter dx is used for the central finite difference approximation
Has two limitations; one the check performs an unnecessary adjoint simulation. Two the check_gradient is only available for regular optimizations and not superoptimizations of multiple FOMs.
Superoptimization
The + operator has been overloaded in the optimization class so it is trivial to simultaneously optimize:
- Different output waveguides and\or wavelength bands CWDM
- Ensure balanced TE\TM performance or suppress one over the other
- Robust manufacturing by simultaneously optimizing underetch\overtech\nominal variations.
- Etc ...
Simply adds the various Optimization classes together to create a new SuperOptimization object. Then you will call run on this object to co-optimize the various optimization definitions. Each FOM calculation requires 1 optimization object so the number of simulations at each iteration will be \( N_{sim} = 2\times N_{FOM} \).
Example
opt = opt_TE + opt_TM
opt.run(working_dir = working_dir)