In this example, we use the topology optimization feature from the inverse design toolbox lumopt to design a wavelength demultiplexer for 4 channels. We target a 10nm transmission band around the center wavelength of 1270nm, 1290m, 1310nm and 1330nm. Between the channels, there is a 10nm gap for isolation. In this example, we restrict the design area to 6um x 6um footprint.
Run and Results
Download the file CWDM_splitter_1310_4ch_2D_TE_topology.zip and unzip all the files into one common directory.
To speed up the optimization, it is recommended to set the resource configuration of FDTD such that the job manager can run several jobs in parallel. Since each job is a relatively small 2d simulation, it makes sense to configure several resources which use one thread each. For example, if the computer that will run the optimization has 8 physical cores, it makes sense to add 8 resources with one core each as shown in the screenshot below. In this example, there is no point in having more than 8 resources. So, if the computer has 16 cores, it makes sense to add 8 resources with two cores each. Not all license types will allow this many parallel computation so you may need to test this.
Running the python script then starts the optimization. This example is complex and the full optimization runs for about 1000 iterations. Depending on the available compute resources, this can take several hours up to a few days.
Important Model Settings
The initial condition
In this example, we initially fill the design area with a fictitious average material parameter, which often results in solutions with good performance. However, depending on the FOM and other settings, it is possible that a different starting condition will yield better results
Maximum number of iterations, especially during the binarization phase
The 4-channel wavelength demultiplexer is a complex device and the optimization requires significantly more iterations than simpler devices (such as the Y-splitter). In this example, we increased the number of iterations for the initial grayscale phase to 500 and we increased the maximum number of iterations for each binarization step to 50. Further increasing those values will lead to longer optimization times but can also further improve the results.
The structure itself can be changed by modifying the footprint and also the smoothing filter radius as shown below:
Warning: Along the y-axis, the structure should not be less than 5um tall because the optimization region won’t be connected to the output waveguides anymore.
Since we use a local, gradient-based optimizer, the result often strongly depends on the initial guess. The given example has four different initial conditions built in to try:
By uncommenting any of the lines, one can try different initial conditions. In addition, it is possible to provide custom initial conditions such as random noise or specific structures.
Convergence parameters of the optimizer
Lumopt has a number of parameters which determine the progress of the optimizer. Changing them often allows to make trade-offs between the best results and computations time. Here are some of the most important ones:
max_iter: Determines after how many iterations, the greyscale phase ends. The optimizer can terminate before this number due to other criteria (e.g. see below) but it will never perform more iterations. Increasing this number will often require more computational time but can lead to better performance. The default value is 400 in this example.
tfol: The minimum require change in the figure of merit (FOM) between two subsequent iterations for the greyscale optimization to continue. The default value is 1e-5 in this example:
continuation_max_iter: The maximum number of iterations for each binarization step. If not specified, the default is 20 iterations but for complicated structures, explicitly setting a larger number as shown below can lead to better optimization results (at the cost of more simulation time):
opt.continuation_max_iter = 40 #< How many iterations per binarization step (default is 20)
beta: After each binarization step, the new beta value is computed by
βnew=βold∗βfactor.βnew=βold∗βfactor. The default value is beta=1.2 but you can set other values as shown below. Increasing beta can make the optimization faster. Decreasing can give better optimization results but at the cost of longer compute times.
If the optimization is disrupted and you would like to continue, or you want to try slightly different parameters with the last valid result you can load the previous geometry as the initial conditions.
prev_filename='<path to old optimization>/parameters_292.npz' #< Parameters of the iteration to load prev_geom = TopologyOptimization2D.from_file(prev_filename) params = prev_geom.last_params beta = prev_geom.beta runSim(params, ...)
Note: It is not possible to exactly continue the optimization where it failed. The main reason is that the L-BFGS optimizer builds up some internal state based on the optimization history which is not saved. If the optimization was already in the binarization phase (i.e. beta>1) you may want to reduce the number of iterations for the first phase of the restart.