Skip to content

Commit

Permalink
fixes and tweaks for tutorial on shape optimization (#2881)
Browse files Browse the repository at this point in the history
  • Loading branch information
oskooi authored Aug 10, 2024
1 parent d0bbf19 commit d63fd06
Showing 1 changed file with 4 additions and 5 deletions.
9 changes: 4 additions & 5 deletions doc/docs/Python_Tutorials/Adjoint_Solver.md
Original file line number Diff line number Diff line change
Expand Up @@ -1232,21 +1232,21 @@ if __name__ == "__main__":
Shape Optimization of a Multilayer Stack
----------------------------------------

We extend the demonstration of the shape derivative from the previous tutorial to perform shape optimization of a multilayer stack over a broad bandwidth. The 1D design problem is shown in the schematic below and involves finding the layer thicknesses for a fixed number of layers (9) which minimize the integrated field intensity $\int |E_x|^2$, which demonstrates a `FourierFields` objective function. (This is equivalent to minimizing absorption if $\varepsilon$ had a small imaginary part, and is related but not precisely equivalent to minimizing transmission or maximizing reflection.) In particular, we minimize the worst case (largest) of the intensities at two wavelengths: $\lambda_1$ = 0.95 μm and $\lambda_2$ = 1.05 μm, to roughly emulate a broadband problem.
We extend the demonstration of the shape derivative from the previous tutorial to perform shape optimization of a multilayer stack over a broad bandwidth. The 1D design problem is shown in the schematic below and involves finding the layer thicknesses for a fixed number of layers (9) which minimize the integrated field intensity $\int |E_x|^2$ using a `FourierFields` objective function. (This is equivalent to minimizing absorption if $\varepsilon$ had a small imaginary part, and is related but not precisely equivalent to minimizing transmission or maximizing reflection.) In particular, we minimize the worst case (largest) of the intensities at two wavelengths: $\lambda_1$ = 0.95 μm and $\lambda_2$ = 1.05 μm, to roughly emulate a broadband problem.

The stack consists of two materials of alternating refractive index $n_A$ = 1.3 and $n_B$ = 1.0. The layers are arranged as $n_A$, $n_B$, $n_A$, $n_B$, ..., $n_A$. The semi-infinite regions to the left and right of the stack are vacuum.

![](../images/multilayer_stack_shape_opt.png#center)

A reference design to compare our results against is a [quarter-wavelength stack](https://en.wikipedia.org/wiki/Distributed_Bragg_reflector). The mean wavelength of $\lambda_1$ and $\lambda_2$ is $\lambda = 1.0 μm for which the quarter-wavelength layer thicknesses are $\lambda / (4 n_A)$ = 0.19 μm and $\lambda / (4 n_B)$ = 0.25 μm. These values are used to specify upper and lower bounds for the layer thicknesses. This is important given the non-convex nature of this particular design problem.
A reference design to compare our results against is a [quarter-wavelength stack](https://en.wikipedia.org/wiki/Distributed_Bragg_reflector). The mean wavelength of $\lambda_1$ and $\lambda_2$ is $\lambda$ = 1.0 μm for which the quarter-wavelength layer thicknesses are $\lambda / (4 n_A)$ = 0.19 μm and $\lambda / (4 n_B)$ = 0.25 μm. These values are used to specify upper and lower bounds for the layer thicknesses. This is important given the non-convex nature of this particular design problem. The transmittance for $\lambda_1$, $\lambda_2$, and $\lambda$ = 1.0 μm for the quarter-wavelength stack at $\lambda$ = 1.0 μm are 0.2925, 0.2865, and 0.2522, respectively. Note that this relatively large transmission is due to the index contrast of 1.3. For comparison, the transmittance for a quarter-wavelength stack at $\lambda$ = 1.0 μm with nine layers for a larger index contrast of 3.5 is 1e-5.

The worst-case optimization is implemented using the [epigraph formulation](https://nlopt.readthedocs.io/en/latest/NLopt_Introduction/#equivalent-formulations-of-optimization-problems) and the Conservative Convex Separable Approximation (CCSA) algorithm. The optimization is run ten times with random initial designs from which the local optimum with the smallest objective value is chosen. For the run involving the best design, a plot of the objective function vs. iteration number for the two wavelengths is shown below. Also included in this plot is the epigraph variable. These results demonstrate that the performance of the stack is limited by that of the shorter wavelength $\lambda$ = 0.95 μm, so the $\lambda$ = 1.05 μm performance does not directly affect the result. We observed this trend for various local optima. The optimizer converges to the local optimum after 13 iterations. The nine layer thicknesses of this design are (in μm): 0.1822, 0.2369, 0.1822, 0.2369, 0.1822, 0.2369, 0.1910, 0.2504, 0.1931. Since these are 1D simulations, the runtime for each design iteration is generally fast (about ten seconds using one core of an Intel Xeon i7-7700K CPU @ 4.20GHz).
The worst-case optimization is implemented using the [epigraph formulation](https://nlopt.readthedocs.io/en/latest/NLopt_Introduction/#equivalent-formulations-of-optimization-problems) and the Conservative Convex Separable Approximation (CCSA) algorithm. The optimization is run ten times with random initial designs from which the local optimum with the smallest objective value is chosen. For the run involving the best design, a plot of the objective function vs. iteration number for the two wavelengths is shown below. Also included in this plot is the epigraph variable. These results demonstrate that the performance of the stack is limited by that of the shorter wavelength $\lambda_1$, so the $\lambda_2$ performance does not directly affect the result. We observed this trend for various local optima. The optimizer converges to the local optimum after 13 iterations. The nine layer thicknesses of this design are (in μm): 0.1822, 0.2369, 0.1822, 0.2369, 0.1822, 0.2369, 0.1910, 0.2504, 0.1931. Since these are 1D simulations, the runtime for each design iteration is generally fast (about ten seconds using a single core of an Intel Xeon i7-7700K CPU @ 4.20GHz).

In some cases (not shown), the optimizer may take some steps that make things *worse* during the early iterations. This is a common phenomenon in many algorithms — initially, the optimizer takes steps that are too large and then has to backtrack. After a few iterations, the algorithm has more information about a "good" step size.

![](../images/multilayer_opt_obj_func_history.png#center)

Finally, a plot of $|E_x|^2$ in the stack (design region) normalized by the fields from the quarter-wavelength stack is shown below. The plot shows that while the DFT fields for each of the two wavelengths are decaying through the stack, the transmittance values are different: T($\lambda_1$ = 0.95 μm) = 0.2574 vs. T($\lambda_2$ = 1.05 μm) = 0.3734. The transmittance for the quarter-wavelength stack at $\lambda \approx$ 1.0 μm is 0.2522. Note that the magnitude of $|E_x|^2$ at the right edge of the stack is larger for $\lambda_2$ = 1.05 μm than $\lambda_1$ = 0.95 μm. This is consistent with the transmittance being larger for $\lambda_2$ = 1.05 than for $\lambda_1$ = 0.95 μm.
Finally, a plot of $|E_x|^2$ in the stack (design region) normalized by the fields from the quarter-wavelength stack at $\lambda$ = 1.0 μm is shown below. The plot shows that while the DFT fields for each of the two wavelengths are decaying through the stack, the transmittance values are different: T($\lambda_1$ = 0.95 μm) = 0.2574 vs. T($\lambda_2$ = 1.05 μm) = 0.3734. Note that the magnitude of $|E_x|^2$ at the right edge of the stack is larger for $\lambda_2$ = 1.05 μm than $\lambda_1$ = 0.95 μm. This is consistent with the transmittance being larger for $\lambda_2$ = 1.05 than for $\lambda_1$ = 0.95 μm.

![](../images/broadband_field_decay_in_multilayer_stack.png#center)

Expand Down Expand Up @@ -1647,7 +1647,6 @@ if __name__ == "__main__":
min_epigraph_and_layer_thickness_um = epigraph_and_layer_thickness_um
min_obj_func_history = copy.deepcopy(obj_func_history)
min_epigraph_variable_history = copy.deepcopy(epigraph_variable_history)
print(f"new_minima:, {j:2d}, {min_obj_val}")


# Save important optimization parameters and output for post processing.
Expand Down

0 comments on commit d63fd06

Please sign in to comment.