diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/01-Introduction-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/01-Introduction-checkpoint.ipynb deleted file mode 100644 index 311f3b285..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/01-Introduction-checkpoint.ipynb +++ /dev/null @@ -1,477 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Meep Adjoint Solver - Introduction\n", - "\n", - "This tutorial serves as a basic introduction to MEEP's adjoint solver. The adjoint solver provides a simple and flexible interface to calculating \"sensitivities\" or gradients of user specified objective functions with respect to **any number** of design variables -- **all with the cost of just two simulations.**\n", - "\n", - "Practical electromagnetic design problems are often constrained by complicated design requirements that aren't easily satisfied with physical intuition. We often formulate our problem as functions of Fourier transformed fields, (e.g. S-parameters, Poynting Fluxes, mode overlap coefficients, etc.). We can \"inverse design\" a structure that maximizes (or minimizes) this cost function with various optimization algorithms. The adjoint method provides an extremely cheap gradient, accelerating many out of the box optimization algorithms.\n", - "\n", - "In this tutorial, we'll demonstrate how you can quickly begin leveraging this interface. As a toy example, we'll examine a 2D integrated photonic waveguide bend. We'll code up a cost function that will tell our optimizer to maximize power transport around the bend. We'll discretize the bend into several individual pixels and calculate the gradient of this cost function w.r.t. all these pixels. Finally, we'll compare our gradients with finite difference approximates, which are much more expensive to calculate. Hopefully by the end of the tutorial, you appreciate the simplicity and flexibility this particular package offers. \n", - "\n", - "First, we'll import `meep` and `autograd`, a widely used automatic differentiation package. `autograd` wraps around `numpy` and allows us to quickly differentiate functions composed of standard `numpy` routines. This will be especially useful when we want to formulate our objective function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "from matplotlib import pyplot as plt\n", - "\n", - "mp.quiet(quietval=True)\n", - "seed = 240\n", - "np.random.seed(seed)\n", - "Si = mp.Medium(index=3.4)\n", - "SiO2 = mp.Medium(index=1.44)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll begin to specify the domain of our waveguide bend simulation, just as we would with any other simulation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "resolution = 30\n", - "\n", - "Sx = 6\n", - "Sy = 5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "pml_layers = [mp.PML(1.0)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll then define our sources. We'll use a narrowband Gaussian pulse, even though our objective function will only operate over a single wavelength (for this example). While we could use the CW solver, it's often faster (and more dependable) to use the timestepping routines and a narrowband pulse." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fcen = 1 / 1.55\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [-1, 0, 0]\n", - "source_size = mp.Vector3(0, 2, 0)\n", - "kpoint = mp.Vector3(1, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [\n", - " mp.EigenModeSource(\n", - " src,\n", - " eig_band=1,\n", - " direction=mp.NO_DIRECTION,\n", - " eig_kpoint=kpoint,\n", - " size=source_size,\n", - " center=source_center,\n", - " )\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we'll define our waveguide geometry and \"design region\". The design region takes a 10x10 grid of randomly generated design variables and maps them to a point within the specified volume. Since meep operates under the \"illusion of continuitiy\", it's important we provide an interpolator to perform this mapping.\n", - "\n", - "In this case, we'll use a builtin bilinear interpolator to take care of this for us. You can use whatever interpolation function you want, provided it can return either a medium or permittivity (as described in the manual) and you can calculate the gradient of the interpolator with respect to the inputs (often just a matrix multiplication). The built-in interpolator takes care of all of this for us." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "design_region_resolution = 10\n", - "Nx = design_region_resolution\n", - "Ny = design_region_resolution\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables, volume=mp.Volume(center=mp.Vector3(), size=mp.Vector3(1, 1, 0))\n", - ")\n", - "\n", - "\n", - "geometry = [\n", - " mp.Block(\n", - " center=mp.Vector3(x=-Sx / 4), material=Si, size=mp.Vector3(Sx / 2, 0.5, 0)\n", - " ), # horizontal waveguide\n", - " mp.Block(\n", - " center=mp.Vector3(y=Sy / 4), material=Si, size=mp.Vector3(0.5, Sy / 2, 0)\n", - " ), # vertical waveguide\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ), # design region\n", - " mp.Block(\n", - " center=design_region.center,\n", - " size=design_region.size,\n", - " material=design_variables,\n", - " e1=mp.Vector3(x=-1).rotate(mp.Vector3(z=1), np.pi / 2),\n", - " e2=mp.Vector3(y=1).rotate(mp.Vector3(z=1), np.pi / 2),\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now formulate or simulation object" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def mapping(x):\n", - " x = x.reshape(Nx, Ny)\n", - " x = (npa.flipud(x) + x) / 2 # left-right symmetry\n", - " x = (npa.fliplr(x) + x) / 2 # up-down symmetry\n", - " x = npa.rot90(x) + x # 90-degree symmetry\n", - " return x" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " eps_averaging=False,\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll start defining our objective function. Objective functions must be composed of \"field functions\" that transform the recorded fields. Right now, only the Eigenmode Decomposition monitors are readily accessible from the adjoint API. That being said, it is easy to extend the API to other field functions, like Poynting fluxes.\n", - "\n", - "In our case, we just want to maximize the power in the fundamental mode at the top of the waveguide bend. We'll define a new object that specifies these details. We'll also create a list of our objective \"quantities\" (just one in our case)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "TE0 = mpa.EigenmodeCoefficient(\n", - " sim, mp.Volume(center=mp.Vector3(0, 1, 0), size=mp.Vector3(x=2)), mode=1\n", - ")\n", - "ob_list = [TE0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Our objective function will take the magnitude squared of our predefined objective quantity. We'll define the function as a normal python function. We'll use dummy parameters that map sequentially to the list of objective quantities we defined above. We'll also use autograd's version of numpy so that the adjoint solver can easily differentiate our objective function with respect to each of these quantities. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def J(alpha):\n", - " return npa.abs(alpha) ** 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now define an `OptimizationProblem` using our simulation object, objective function, and objective quantities (or arguments). We'll also tell the solver to examine the `Ez` component of the Fourier transformed fields. The solver will stop the simulation after these components have stopped changing by a certain relative threshold (default is 1e-6)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=J,\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " fcen=fcen,\n", - " df=0,\n", - " nf=1,\n", - " decay_fields=[mp.Ez],\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will now initialize our object with a set of random design parameters. We'll use `numpy`'s `random` library to generate a uniform random variable between 1 and 12, to correspond to the refractive index of air and silicon." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x0 = np.random.rand(Nx * Ny)\n", - "opt.update_design([x0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we can visualize our final simulation domain with any extra monitors as defined by our objective function. This `plot2D` function is just like `Simulation`'s `plot2D`, only it takes an additional first argument. We'll set it to `True` to tell the solver to initialize the solver and clear any stored fields." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.plot2D(True, frequency=1 / 1.55)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see an expected monitor on the top of the waveguide, but we also see an additional monitor spanning our design region. This monitor records the Fourier transformed fields needed to calulcate the gradient.\n", - "\n", - "Since everything looks good, we can go now calculate the gradient and the cost function evaluation. We do so by calling our solver object directly. The object returns the objective function evaluation, `f0`, and the gradient, `dJ_du`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f0, dJ_du = opt()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can visualize these gradients." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.imshow(np.rot90(dJ_du.reshape(Nx, Ny)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We now have a gradient with respect to our design parameters. To verify the accuracy of our method, we'll perform a finite difference approximation. \n", - "\n", - "Luckily, our solver has a built finite difference method (`calculate_fd_gradient`). Since the finite difference approximates require several expensive simulations, we'll only estimate 20 of them (randomly sampled by our function)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "db = 1e-3\n", - "choose = 10\n", - "g_discrete, idx = opt.calculate_fd_gradient(num_gradients=choose, db=db)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare the results by fitting a line to the two gradients" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "(m, b) = np.polyfit(np.squeeze(g_discrete), dJ_du[idx], 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "and subsequently plot the results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "min_g = np.min(g_discrete)\n", - "max_g = np.max(g_discrete)\n", - "\n", - "fig = plt.figure(figsize=(12, 6))\n", - "\n", - "plt.subplot(1, 2, 1)\n", - "plt.plot([min_g, max_g], [min_g, max_g], label=\"y=x comparison\")\n", - "plt.plot([min_g, max_g], [m * min_g + b, m * max_g + b], \"--\", label=\"Best fit\")\n", - "plt.plot(g_discrete, dJ_du[idx], \"o\", label=\"Adjoint comparison\")\n", - "plt.xlabel(\"Finite Difference Gradient\")\n", - "plt.ylabel(\"Adjoint Gradient\")\n", - "plt.legend()\n", - "plt.grid(True)\n", - "plt.axis(\"square\")\n", - "\n", - "plt.subplot(1, 2, 2)\n", - "rel_err = (\n", - " np.abs(np.squeeze(g_discrete) - np.squeeze(dJ_du[idx]))\n", - " / np.abs(np.squeeze(g_discrete))\n", - " * 100\n", - ")\n", - "plt.semilogy(g_discrete, rel_err, \"o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Finite Difference Gradient\")\n", - "plt.ylabel(\"Relative Error (%)\")\n", - "\n", - "fig.tight_layout(rect=[0, 0.03, 1, 0.95])\n", - "fig.suptitle(\"Resolution: {} Seed: {} Nx: {} Ny: {}\".format(resolution, seed, Nx, Ny))\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We notice strong agreement between the adjoint gradients and the finite difference gradients. Let's bump up the resolution to see if the results are consistent." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "resolution = 20\n", - "opt.sim.resolution = resolution\n", - "f0, dJ_du = opt()\n", - "g_discrete, idx = opt.calculate_fd_gradient(num_gradients=choose, db=db)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "min_g = np.min(g_discrete)\n", - "max_g = np.max(g_discrete)\n", - "\n", - "fig = plt.figure(figsize=(12, 6))\n", - "\n", - "plt.subplot(1, 2, 1)\n", - "plt.plot([min_g, max_g], [min_g, max_g], label=\"y=x comparison\")\n", - "plt.plot([min_g, max_g], [m * min_g + b, m * max_g + b], \"--\", label=\"Best fit\")\n", - "plt.plot(g_discrete, dJ_du[idx], \"o\", label=\"Adjoint comparison\")\n", - "plt.xlabel(\"Finite Difference Gradient\")\n", - "plt.ylabel(\"Adjoint Gradient\")\n", - "plt.legend()\n", - "plt.grid(True)\n", - "plt.axis(\"square\")\n", - "\n", - "plt.subplot(1, 2, 2)\n", - "rel_err = (\n", - " np.abs(np.squeeze(g_discrete) - np.squeeze(dJ_du[idx]))\n", - " / np.abs(np.squeeze(g_discrete))\n", - " * 100\n", - ")\n", - "plt.semilogy(g_discrete, rel_err, \"o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Finite Difference Gradient\")\n", - "plt.ylabel(\"Relative Error (%)\")\n", - "\n", - "fig.tight_layout(rect=[0, 0.03, 1, 0.95])\n", - "fig.suptitle(\"Resolution: {} Seed: {} Nx: {} Ny: {}\".format(resolution, seed, Nx, Ny))\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/02-Waveguide_Bend-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/02-Waveguide_Bend-checkpoint.ipynb deleted file mode 100644 index c2e360d25..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/02-Waveguide_Bend-checkpoint.ipynb +++ /dev/null @@ -1,412 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Waveguide Bend Optimization\n", - "\n", - "In this tutorial, we'll examine how we can use Meep's adjoint solver to optimize for a particular design criteria. Specifcally, we'll maximize the transmission around a silicon waveguide bend. This tutorial will illustrate the adjoint solver's ability to quickly calculate gradients for objective functions with multiple objective quantities.\n", - "\n", - "To begin, we'll import meep, our adjoint module, `autograd` (as before) and we will also import `nlopt`, a nonlinear optimization package." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "import nlopt\n", - "from matplotlib import pyplot as plt\n", - "\n", - "mp.verbosity(0)\n", - "Si = mp.Medium(index=3.4)\n", - "SiO2 = mp.Medium(index=1.44)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll build a 90 degree bend waveguide with a design region that is 1 micron by 1 micron. We'll discretize our region into a 10 x 10 grid (100 total parameters). We'll send in a narrowband gaussian pulse centered at 1550 nm. We'll also use the same objective function and optimizer object as before." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "resolution = 20\n", - "\n", - "Sx = 6\n", - "Sy = 5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "pml_layers = [mp.PML(1.0)]\n", - "\n", - "fcen = 1 / 1.55\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [-1.5, 0, 0]\n", - "source_size = mp.Vector3(0, 2, 0)\n", - "kpoint = mp.Vector3(1, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [\n", - " mp.EigenModeSource(\n", - " src,\n", - " eig_band=1,\n", - " direction=mp.NO_DIRECTION,\n", - " eig_kpoint=kpoint,\n", - " size=source_size,\n", - " center=source_center,\n", - " )\n", - "]\n", - "\n", - "design_region_resolution = 10\n", - "Nx = design_region_resolution\n", - "Ny = design_region_resolution\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables, volume=mp.Volume(center=mp.Vector3(), size=mp.Vector3(1, 1, 0))\n", - ")\n", - "\n", - "\n", - "geometry = [\n", - " mp.Block(\n", - " center=mp.Vector3(x=-Sx / 4), material=Si, size=mp.Vector3(Sx / 2, 0.5, 0)\n", - " ), # horizontal waveguide\n", - " mp.Block(\n", - " center=mp.Vector3(y=Sy / 4), material=Si, size=mp.Vector3(0.5, Sy / 2, 0)\n", - " ), # vertical waveguide\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ), # design region\n", - " # mp.Block(center=design_region.center, size=design_region.size, material=design_variables,\n", - " # e1=mp.Vector3(x=-1).rotate(mp.Vector3(z=1), np.pi/2), e2=mp.Vector3(y=1).rotate(mp.Vector3(z=1), np.pi/2))\n", - " #\n", - " # The commented lines above impose symmetry by overlapping design region with the same design variable. However,\n", - " # currently there is an issue of doing that; We give an alternative approach to impose symmetry in later tutorials.\n", - " # See https://github.com/NanoComp/meep/issues/1984 and https://github.com/NanoComp/meep/issues/2093\n", - "]\n", - "\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " eps_averaging=False,\n", - " resolution=resolution,\n", - ")\n", - "\n", - "TE_top = mpa.EigenmodeCoefficient(\n", - " sim, mp.Volume(center=mp.Vector3(0, 1, 0), size=mp.Vector3(x=2)), mode=1\n", - ")\n", - "ob_list = [TE_top]\n", - "\n", - "\n", - "def J(alpha):\n", - " return npa.abs(alpha) ** 2\n", - "\n", - "\n", - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=J,\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " fcen=fcen,\n", - " df=0,\n", - " nf=1,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As before, we'll visualize everything to ensure our monitors, boundary layers, and geometry are drawn correctly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x0 = 0.5 * np.ones((Nx * Ny,))\n", - "opt.update_design([x0])\n", - "\n", - "opt.plot2D(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now define a cost function wrapper that we can feed into our `nlopt` optimizer. `nlopt` expects a python function where the gradient is passed in place. In addition, we'll update a list with every objective function call so we can track the cost function evolution each iteration.\n", - "\n", - "Notice the `opt` adjoint solver object requires we pass our numpy array of design parameters within an additional list. This is because the adjoint solver can solve for multiple design regions simultaneously. It's useful to break up each region's parameters into indvidual numpy arrays. In this simple example, we only have one design region." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "sensitivity = [0]\n", - "\n", - "\n", - "def f(x, grad):\n", - " f0, dJ_du = opt([x])\n", - " f0 = f0[0] # f0 is an array of length 1\n", - " if grad.size > 0:\n", - " grad[:] = np.squeeze(dJ_du)\n", - " evaluation_history.append(np.real(f0))\n", - " sensitivity[0] = dJ_du\n", - " return np.real(f0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can set up the actual optimizer engine. We'll select the Method of Moving Asymptotes because it's a gradient based method that allows us to specify linear and nonlinear constraints. For now, we'll simply bound our parameters between 0 and 1.\n", - "\n", - "We'll tell our solver to maximize (rather than minimize) our cost function, since we are trying to maximize the power transmission around the bend.\n", - "\n", - "We'll also tell the optimizer to stop after 10 function calls. This will keep the wait time short and demonstrate how powerful the adjoint solver is." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny\n", - "maxeval = 10\n", - "\n", - "solver = nlopt.opt(algorithm, n)\n", - "solver.set_lower_bounds(0)\n", - "solver.set_upper_bounds(1)\n", - "solver.set_max_objective(f)\n", - "solver.set_maxeval(maxeval)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = solver.optimize(x0);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that the solver is done running, we can evaluate our progress." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(evaluation_history, \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"FOM\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can update our optimization object and visualize the results." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([x])\n", - "opt.plot2D(\n", - " True,\n", - " plot_monitors_flag=False,\n", - " output_plane=mp.Volume(center=(0, 0, 0), size=(2, 2, 0)),\n", - ")\n", - "plt.axis(\"off\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We quickly see that the solver improves the design, but because of the way we chose to formulate our cost function, it's difficult to quantify our results. After all, how good is a figure of merit (FOM) of 70?\n", - "\n", - "To overcome this, we'll slightly modify our objective function to include an extra monitor just after the source. This monitor will track however much power is transmitted into the waveguide. We can then normalize the upper monitor's response by this parameter:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "TE0 = mpa.EigenmodeCoefficient(\n", - " sim, mp.Volume(center=mp.Vector3(-1, 0, 0), size=mp.Vector3(y=2)), mode=1\n", - ")\n", - "ob_list = [TE0, TE_top]\n", - "\n", - "\n", - "def J(source, top):\n", - " return npa.abs(top / source) ** 2\n", - "\n", - "\n", - "opt.objective_functions = [J]\n", - "opt.objective_arguments = ob_list\n", - "opt.update_design([x0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll refresh our solver and try optimizing again:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "solver = nlopt.opt(algorithm, n)\n", - "solver.set_lower_bounds(0)\n", - "solver.set_upper_bounds(1)\n", - "solver.set_max_objective(f)\n", - "solver.set_maxeval(maxeval)\n", - "x = solver.optimize(x0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can view our results and normalize the FOM as the percent power transmission." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(np.array(evaluation_history) * 100, \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"Transmission (%)\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We once again clearly see great improvement, from about 5% transmission to about 85%, after just 10 iterations!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "improvement = max(evaluation_history) / min(evaluation_history)\n", - "print(\"Achieved an improvement of {0:1.1f}x\".format(improvement))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can visualize our new geometry. We see that the design region naturally connected the two waveguide segements in a bend fashion and has placed several other distinctive features around the curve." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([x])\n", - "opt.plot2D(\n", - " True,\n", - " plot_monitors_flag=False,\n", - " output_plane=mp.Volume(center=(0, 0, 0), size=(2, 2, 0)),\n", - ")\n", - "plt.axis(\"off\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also visualize the sensitivity to see which geometric areas are most sensitive to perturbations." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.imshow(np.rot90(np.squeeze(np.abs(sensitivity[0].reshape(Nx, Ny)))));" - ] - } - ], - "metadata": { - "@webio": { - "lastCommId": null, - "lastKernelId": null - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/03-Filtered_Waveguide_Bend-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/03-Filtered_Waveguide_Bend-checkpoint.ipynb deleted file mode 100644 index 7a759ed66..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/03-Filtered_Waveguide_Bend-checkpoint.ipynb +++ /dev/null @@ -1,580 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Filtering and Thresholding Design Parameters and Broadband Objective Functions\n", - "\n", - "While cheap gradients are always appreciated, we noticed that the optimizer and adjoint solver often produce devices that are impossible to fabricate. They tend to continuously vary the refractive index and produce small feature sizes. Furthermore, the lack of constraint often throws the optimizer into local minima, stunting the overall progress of the solver.\n", - "\n", - "To overcome this, the Topology Optimization (TO) community often implements linear and nonlinear functional transforms on the design parameters before projecting them onto the simulation domain. For example, we can blur many of the small design parameters together using a conic filter, and subsequently threshold using sigmoid like functions.\n", - "\n", - "The resulting parameters can include constraints, like minimum lengths scales, and project the cost function into a more friendly (and sometimes less friendly) design space.\n", - "\n", - "We'll examine how to accomplish these goals using native `autograd` functions and meep's adjoint solver." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "from autograd import tensor_jacobian_product, grad\n", - "import nlopt\n", - "from matplotlib import pyplot as plt\n", - "from matplotlib.patches import Circle\n", - "from scipy import special, signal\n", - "\n", - "mp.verbosity(0)\n", - "Si = mp.Medium(index=3.4)\n", - "SiO2 = mp.Medium(index=1.44)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's choose our basic geometry parameters, along with the frequency range to simulate. If our frequency points are too close together than the resulting adjoint simulation may take longer to simulate each iteration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "waveguide_width = 0.5\n", - "design_region_width = 2.5\n", - "design_region_height = 2.5\n", - "\n", - "waveguide_length = 0.5\n", - "\n", - "pml_size = 1.0\n", - "\n", - "resolution = 30\n", - "\n", - "frequencies = 1 / np.linspace(1.5, 1.6, 10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we'll specify our smoothing filter radius (in microns). This is determined by our minimum length scale and erosion threshold point. In this example, we won't actually enforce the length scale constraints, but the following machinery is required to do so.\n", - "\n", - "We also need to specify the number of design parameters per meep pixel we want to set up." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "minimum_length = 0.09 # minimum length scale (microns)\n", - "eta_i = (\n", - " 0.5 # blueprint (or intermediate) design field thresholding point (between 0 and 1)\n", - ")\n", - "eta_e = 0.55 # erosion design field thresholding point (between 0 and 1)\n", - "eta_d = 1 - eta_e # dilation design field thresholding point (between 0 and 1)\n", - "filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)\n", - "print(filter_radius)\n", - "design_region_resolution = int(1 * resolution)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As before, we'll generate our simulation cell with the specified sources, boundary layers, geometry, etc." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Sx = 2 * pml_size + 2 * waveguide_length + design_region_width\n", - "Sy = 2 * pml_size + design_region_height + 0.5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "pml_layers = [mp.PML(pml_size)]\n", - "\n", - "fcen = 1 / 1.55\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [-Sx / 2 + pml_size + waveguide_length / 3, 0, 0]\n", - "source_size = mp.Vector3(0, Sy, 0)\n", - "kpoint = mp.Vector3(1, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [\n", - " mp.EigenModeSource(\n", - " src,\n", - " eig_band=1,\n", - " direction=mp.NO_DIRECTION,\n", - " eig_kpoint=kpoint,\n", - " size=source_size,\n", - " center=source_center,\n", - " )\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's set up our design region. The defauly grid type behaves just like any other geometry object -- the last object in the tree overides any objects underneath it. In some cases, it's important to *average* overlapping design regions, i.e. to enforce particular symmetries. To enable this capability, we'll set our design variables to use the `U_MEAN` grid type." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Nx = int(design_region_resolution * design_region_width)\n", - "Ny = int(design_region_resolution * design_region_height)\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables,\n", - " volume=mp.Volume(\n", - " center=mp.Vector3(),\n", - " size=mp.Vector3(design_region_width, design_region_height, 0),\n", - " ),\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The optimizer doesn't know anything about the geometry outside of the design region. We can \"hint\" what parts of the design must be waveguides by forcing boundry constraints. We'll build bitmasks that map the border of the design regions to either silicon or silicon dioxide." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_g = np.linspace(-design_region_width / 2, design_region_width / 2, Nx)\n", - "y_g = np.linspace(-design_region_height / 2, design_region_height / 2, Ny)\n", - "X_g, Y_g = np.meshgrid(x_g, y_g, sparse=True, indexing=\"ij\")\n", - "\n", - "left_wg_mask = (X_g == -design_region_width / 2) & (np.abs(Y_g) <= waveguide_width / 2)\n", - "top_wg_mask = (Y_g == design_region_width / 2) & (np.abs(X_g) <= waveguide_width / 2)\n", - "Si_mask = left_wg_mask | top_wg_mask\n", - "\n", - "border_mask = (\n", - " (X_g == -design_region_width / 2)\n", - " | (X_g == design_region_width / 2)\n", - " | (Y_g == -design_region_height / 2)\n", - " | (Y_g == design_region_height / 2)\n", - ")\n", - "SiO2_mask = border_mask.copy()\n", - "SiO2_mask[Si_mask] = False" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll define various filters that perform smoothing, projection, and material scaling. We'll be sure to use autograd so that we can easily backpropogate our gradient provided by the adjoint solver. We have some smoothing and thresholding filters already defined in the adjoint package that we'll use here." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def mapping(x, eta, beta):\n", - " x = npa.where(Si_mask.flatten(), 1, npa.where(SiO2_mask.flatten(), 0, x))\n", - " # filter\n", - " filtered_field = mpa.conic_filter(\n", - " x,\n", - " filter_radius,\n", - " design_region_width,\n", - " design_region_height,\n", - " design_region_resolution,\n", - " )\n", - "\n", - " # projection\n", - " projected_field = mpa.tanh_projection(filtered_field, beta, eta)\n", - "\n", - " # interpolate to actual materials\n", - " return projected_field.flatten()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's build the geometry and simulation objects. We can force a rotational symmetry by adding extra blocks with the same design variables, but different basis vectors (`e1`, `e2`, etc.) In our case, we need one additional block object rotated by 90 degrees.\n", - "\n", - "Because our design variables are using the `U_MEAN` scheme, the adjoint solver will search for all of the material grids at each point in our design region and average the overlapping design variables. This way, we can enforce our symmetry constraint." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "geometry = [\n", - " mp.Block(\n", - " center=mp.Vector3(x=-Sx / 4), material=Si, size=mp.Vector3(Sx / 2, 0.5, 0)\n", - " ), # horizontal waveguide\n", - " mp.Block(\n", - " center=mp.Vector3(y=Sy / 4), material=Si, size=mp.Vector3(0.5, Sy / 2, 0)\n", - " ), # vertical waveguide\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ), # design region\n", - " mp.Block(\n", - " center=design_region.center,\n", - " size=design_region.size,\n", - " material=design_variables,\n", - " e1=mp.Vector3(x=-1).rotate(mp.Vector3(z=1), np.pi / 2),\n", - " e2=mp.Vector3(y=1).rotate(mp.Vector3(z=1), np.pi / 2),\n", - " ),\n", - "]\n", - "\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=SiO2,\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's formulate our broadband objective function. To keep it simple we'll just minimize the sum of the error, where the error is the L2 norm between the transmission and 1 (i.e. the desired profile)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mode = 1\n", - "\n", - "TE0 = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(x=-Sx / 2 + pml_size + 2 * waveguide_length / 3),\n", - " size=mp.Vector3(y=Sy),\n", - " ),\n", - " mode,\n", - ")\n", - "TE_top = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(0, Sx / 2 - pml_size - 2 * waveguide_length / 3, 0),\n", - " size=mp.Vector3(x=Sx),\n", - " ),\n", - " mode,\n", - ")\n", - "ob_list = [TE0, TE_top]\n", - "\n", - "\n", - "def J(source, top):\n", - " power = npa.abs(top / source)\n", - " return npa.mean(power)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=J,\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " frequencies=frequencies,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, let's see how well our filtering and projection functions work." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rho_vector = np.random.rand(Nx * Ny)\n", - "opt.update_design([mapping(rho_vector, eta_i, 1e3)])\n", - "opt.plot2D(True, output_plane=mp.Volume(center=(0, 0, 0), size=(3, 3, 0)))\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can increase our `beta` term, which controls the thresholding, and simultaneously sweep our perturbation term (`delta`) which is used to generate erosion and dilation geometries typically used in the literature. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "beta = 2048\n", - "\n", - "plt.figure()\n", - "ax1 = plt.subplot(1, 3, 1)\n", - "opt.update_design([mapping(rho_vector, 0.498, beta)])\n", - "opt.plot2D(True, ax=ax1, output_plane=mp.Volume(center=(0, 0, 0), size=(3, 3, 0)))\n", - "plt.title(\"Dilation\")\n", - "\n", - "ax2 = plt.subplot(1, 3, 2)\n", - "opt.update_design([mapping(rho_vector, 0.5, beta)])\n", - "opt.plot2D(True, ax=ax2, output_plane=mp.Volume(center=(0, 0, 0), size=(3, 3, 0)))\n", - "plt.title(\"Intermediate\")\n", - "\n", - "ax3 = plt.subplot(1, 3, 3)\n", - "opt.update_design([mapping(rho_vector, 0.502, beta)])\n", - "opt.plot2D(True, ax=ax3, output_plane=mp.Volume(center=(0, 0, 0), size=(3, 3, 0)))\n", - "plt.title(\"Erosion\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With our framwork in place, we can define our `nlopt` cost function wrapper that also includes our mapping layers and their corresponding gradients. We'll systematically increase our `beta` term so that the thresholding gradually turns on, as suggested by the literature." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "cur_iter = [0]\n", - "\n", - "\n", - "def f(v, gradient, cur_beta):\n", - " print(\"Current iteration: {}\".format(cur_iter[0] + 1))\n", - "\n", - " f0, dJ_du = opt([mapping(v, eta_i, cur_beta)]) # compute objective and gradient\n", - "\n", - " if gradient.size > 0:\n", - " gradient[:] = tensor_jacobian_product(mapping, 0)(\n", - " v, eta_i, cur_beta, np.sum(dJ_du, axis=1)\n", - " ) # backprop\n", - "\n", - " evaluation_history.append(np.real(f0))\n", - "\n", - " plt.figure()\n", - " ax = plt.gca()\n", - " opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - " )\n", - " circ = Circle((2, 2), minimum_length / 2)\n", - " ax.add_patch(circ)\n", - " ax.axis(\"off\")\n", - " plt.show()\n", - "\n", - " cur_iter[0] = cur_iter[0] + 1\n", - "\n", - " return np.real(f0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now run our optimizer in loop. The loop will increase beta and reset the optimizer, which is important since the cost function changes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny # number of parameters\n", - "\n", - "# Initial guess\n", - "x = np.ones((n,)) * 0.5\n", - "x[Si_mask.flatten()] = 1 # set the edges of waveguides to silicon\n", - "x[SiO2_mask.flatten()] = 0 # set the other edges to SiO2\n", - "\n", - "# lower and upper bounds\n", - "lb = np.zeros((Nx * Ny,))\n", - "lb[Si_mask.flatten()] = 1\n", - "ub = np.ones((Nx * Ny,))\n", - "ub[SiO2_mask.flatten()] = 0\n", - "\n", - "cur_beta = 4\n", - "beta_scale = 2\n", - "num_betas = 6\n", - "update_factor = 12\n", - "for iters in range(num_betas):\n", - " solver = nlopt.opt(algorithm, n)\n", - " solver.set_lower_bounds(lb)\n", - " solver.set_upper_bounds(ub)\n", - " solver.set_max_objective(lambda a, g: f(a, g, cur_beta))\n", - " solver.set_maxeval(update_factor)\n", - " x[:] = solver.optimize(x)\n", - " cur_beta = cur_beta * beta_scale" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll wait for a few minutes (or longer) and visualize the results. We see that every time `beta` increases it either drives the cost function out of a local minimum or into a poorer spot. It gets harder to converge as `beta` increases. This is expected as the gradient starts to swing wildy at these thresholded transition regions. Regardless, we are still able to generate a somewhat smoothed structure after just 72 iterations." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(10 * np.log10(evaluation_history), \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"Average Insertion Loss (dB)\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To be sure, we can plot our results and see the resulting geometry." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([mapping(x, eta_i, cur_beta)])\n", - "plt.figure()\n", - "ax = plt.gca()\n", - "opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - ")\n", - "circ = Circle((2, 2), minimum_length / 2)\n", - "ax.add_patch(circ)\n", - "ax.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Of course we want to check the final frequency response. We can run another forward run to pull the objective function parameters and calculate the transmisson." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f0, dJ_du = opt([mapping(x, eta_i, cur_beta)], need_gradient=False)\n", - "frequencies = opt.frequencies\n", - "source_coef, top_coef = opt.get_objective_arguments()\n", - "\n", - "top_profile = np.abs(top_coef / source_coef) ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(1 / frequencies, top_profile * 100, \"-o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Wavelength (microns)\")\n", - "plt.ylabel(\"Transmission (%)\")\n", - "plt.ylim(98, 100)\n", - "plt.show()\n", - "\n", - "plt.figure()\n", - "plt.plot(1 / frequencies, 10 * np.log10(top_profile), \"-o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Wavelength (microns)\")\n", - "plt.ylabel(\"Insertion Loss (dB)\")\n", - "plt.ylim(-0.1, 0)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In summary, it is very easy to implement design constraints and density parameter operations using the native adjoint solver interface. One could use this same design flow to implement robus optimization over many frequency points." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/04-Splitter-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/04-Splitter-checkpoint.ipynb deleted file mode 100644 index ddf535c4b..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/04-Splitter-checkpoint.ipynb +++ /dev/null @@ -1,548 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Design of a Symmetric Broadband Splitter\n", - "\n", - "Many devices of interest can leverage some form of simulation symmetry to reduce the computational cost and storage requirements. The adjoint solver and its corresponding TO filter toolbox are built to work with these symmetries.\n", - "\n", - "To demonstrate this, we look at a symmetric, broadband splitter." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import autograd.numpy as npa\n", - "from autograd import tensor_jacobian_product, grad\n", - "import numpy as np\n", - "from matplotlib import pyplot as plt\n", - "from matplotlib.patches import Circle\n", - "import nlopt\n", - "\n", - "seed = 240\n", - "np.random.seed(seed)\n", - "mp.quiet(quietval=True)\n", - "Si = mp.Medium(index=3.4)\n", - "SiO2 = mp.Medium(index=1.44)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As before, we'll define our geometry, filtering, and wavelength parameters." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "waveguide_width = 0.5\n", - "design_region_width = 2.5\n", - "design_region_height = 2.5\n", - "arm_separation = 1.0\n", - "waveguide_length = 0.5\n", - "pml_size = 1.0\n", - "resolution = 20\n", - "\n", - "minimum_length = 0.09\n", - "eta_e = 0.55\n", - "filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)\n", - "eta_i = 0.5\n", - "eta_d = 1 - eta_e\n", - "design_region_resolution = int(5 * resolution)\n", - "\n", - "frequencies = 1 / np.linspace(1.5, 1.6, 10)\n", - "# print(1/frequencies)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll also define our simulation domain and set up a broadband source." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Sx = 2 * pml_size + 2 * waveguide_length + design_region_width\n", - "Sy = 2 * pml_size + design_region_height + 0.5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "pml_layers = [mp.PML(pml_size)]\n", - "\n", - "fcen = 1 / 1.56\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [-Sx / 2 + pml_size + waveguide_length / 3, 0, 0]\n", - "source_size = mp.Vector3(0, 2, 0)\n", - "kpoint = mp.Vector3(1, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [\n", - " mp.EigenModeSource(\n", - " src,\n", - " eig_band=1,\n", - " direction=mp.NO_DIRECTION,\n", - " eig_kpoint=kpoint,\n", - " size=source_size,\n", - " center=source_center,\n", - " )\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll define our design region. This time, however, we'll include a symmetry across the Y plane (normal direction of the symmetry plane points in the Y direction)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Nx = int(design_region_resolution * design_region_width)\n", - "Ny = int(design_region_resolution * design_region_height)\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables,\n", - " volume=mp.Volume(\n", - " center=mp.Vector3(),\n", - " size=mp.Vector3(design_region_width, design_region_height, 0),\n", - " ),\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll define a filtering and interpolation function. We need to include the symmetry requirements in the filter too." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def mapping(x, eta, beta):\n", - " # filter\n", - " filtered_field = mpa.conic_filter(\n", - " x,\n", - " filter_radius,\n", - " design_region_width,\n", - " design_region_height,\n", - " design_region_resolution,\n", - " )\n", - "\n", - " # projection\n", - " projected_field = mpa.tanh_projection(filtered_field, beta, eta)\n", - "\n", - " # interpolate to actual materials\n", - " return projected_field.flatten()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We also need to define a bitmask that describes the boundary conditions of the waveguide and cladding layers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define spatial arrays used to generate bit masks\n", - "x_g = np.linspace(-design_region_width / 2, design_region_width / 2, Nx)\n", - "y_g = np.linspace(-design_region_height / 2, design_region_height / 2, Ny)\n", - "X_g, Y_g = np.meshgrid(x_g, y_g, sparse=True, indexing=\"ij\")\n", - "\n", - "# Define the core mask\n", - "left_wg_mask = (X_g == -design_region_width / 2) & (np.abs(Y_g) <= waveguide_width / 2)\n", - "top_right_wg_mask = (X_g == design_region_width / 2) & (\n", - " np.abs(Y_g + arm_separation / 2) <= waveguide_width / 2\n", - ")\n", - "bottom_right_wg_mask = (X_g == design_region_width / 2) & (\n", - " np.abs(Y_g - arm_separation / 2) <= waveguide_width / 2\n", - ")\n", - "Si_mask = left_wg_mask | top_right_wg_mask | bottom_right_wg_mask\n", - "\n", - "# Define the cladding mask\n", - "border_mask = (\n", - " (X_g == -design_region_width / 2)\n", - " | (X_g == design_region_width / 2)\n", - " | (Y_g == -design_region_height / 2)\n", - " | (Y_g == design_region_height / 2)\n", - ")\n", - "SiO2_mask = border_mask.copy()\n", - "SiO2_mask[Si_mask] = False" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally we can formulate our geometry and simulation object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "geometry = [\n", - " mp.Block(\n", - " center=mp.Vector3(x=-Sx / 4),\n", - " material=Si,\n", - " size=mp.Vector3(Sx / 2 + 1, waveguide_width, 0),\n", - " ), # left waveguide\n", - " mp.Block(\n", - " center=mp.Vector3(x=Sx / 4, y=arm_separation / 2),\n", - " material=Si,\n", - " size=mp.Vector3(Sx / 2 + 1, waveguide_width, 0),\n", - " ), # top right waveguide\n", - " mp.Block(\n", - " center=mp.Vector3(x=Sx / 4, y=-arm_separation / 2),\n", - " material=Si,\n", - " size=mp.Vector3(Sx / 2 + 1, waveguide_width, 0),\n", - " ), # bottom right waveguide\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ),\n", - " mp.Block(\n", - " center=design_region.center,\n", - " size=design_region.size,\n", - " material=design_variables,\n", - " e2=mp.Vector3(y=-1),\n", - " ),\n", - "]\n", - "\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " symmetries=[mp.Mirror(direction=mp.Y)],\n", - " default_material=SiO2,\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can proceed to define our objective function, its corresponding arguments, and the optimization object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mode = 1\n", - "\n", - "TE0 = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(x=-Sx / 2 + pml_size + 2 * waveguide_length / 3),\n", - " size=mp.Vector3(y=1.5),\n", - " ),\n", - " mode,\n", - ")\n", - "TE_top = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(\n", - " Sx / 2 - pml_size - 2 * waveguide_length / 3, arm_separation / 2, 0\n", - " ),\n", - " size=mp.Vector3(y=arm_separation),\n", - " ),\n", - " mode,\n", - ")\n", - "TE_bottom = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(\n", - " Sx / 2 - pml_size - 2 * waveguide_length / 3, -arm_separation / 2, 0\n", - " ),\n", - " size=mp.Vector3(y=arm_separation),\n", - " ),\n", - " mode,\n", - ")\n", - "ob_list = [TE0, TE_top, TE_bottom]\n", - "\n", - "\n", - "def J(source, top, bottom):\n", - " power = npa.abs(top / source) ** 2 + npa.abs(bottom / source) ** 2\n", - " return npa.mean(power)\n", - "\n", - "\n", - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=J,\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " frequencies=frequencies,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's plot the design and ensure we have symmetry." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "x0 = mapping(\n", - " np.random.rand(\n", - " Nx * Ny,\n", - " ),\n", - " eta_i,\n", - " 128,\n", - ")\n", - "opt.update_design([x0])\n", - "opt.plot2D(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll define a simple objective function that returns the gradient. We'll plot the new geometry after each iteration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "cur_iter = [0]\n", - "\n", - "\n", - "def f(v, gradient, cur_beta):\n", - " print(\"Current iteration: {}\".format(cur_iter[0] + 1))\n", - "\n", - " f0, dJ_du = opt([mapping(v, eta_i, cur_beta)])\n", - "\n", - " plt.figure()\n", - " ax = plt.gca()\n", - " opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - " )\n", - " circ = Circle((2, 2), minimum_length / 2)\n", - " ax.add_patch(circ)\n", - " ax.axis(\"off\")\n", - " plt.savefig(\"media/splitter_{:03d}.png\".format(cur_iter[0]), dpi=300)\n", - " plt.show()\n", - "\n", - " if gradient.size > 0:\n", - " gradient[:] = tensor_jacobian_product(mapping, 0)(\n", - " v, eta_i, cur_beta, np.sum(dJ_du, axis=1)\n", - " )\n", - "\n", - " evaluation_history.append(np.max(np.real(f0)))\n", - "\n", - " cur_iter[0] = cur_iter[0] + 1\n", - "\n", - " return np.real(f0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally we'll run the optimizer." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny # number of parameters\n", - "\n", - "# Initial guess\n", - "x = np.ones((n,)) * 0.5\n", - "x[Si_mask.flatten()] = 1 # set the edges of waveguides to silicon\n", - "x[SiO2_mask.flatten()] = 0 # set the other edges to SiO2\n", - "\n", - "# lower and upper bounds\n", - "lb = np.zeros((Nx * Ny,))\n", - "lb[Si_mask.flatten()] = 1\n", - "ub = np.ones((Nx * Ny,))\n", - "ub[SiO2_mask.flatten()] = 0\n", - "\n", - "cur_beta = 4\n", - "beta_scale = 2\n", - "num_betas = 6\n", - "update_factor = 12\n", - "for iters in range(num_betas):\n", - " print(\"current beta: \", cur_beta)\n", - "\n", - " solver = nlopt.opt(algorithm, n)\n", - " solver.set_lower_bounds(lb)\n", - " solver.set_upper_bounds(ub)\n", - " solver.set_max_objective(lambda a, g: f(a, g, cur_beta))\n", - " solver.set_maxeval(update_factor)\n", - " solver.set_xtol_rel(1e-4)\n", - " x[:] = solver.optimize(x)\n", - " cur_beta = cur_beta * beta_scale" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the optimizer quickly finds a topology that works well and slowly refines it as we continue to \"binarize\" the design." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(10 * np.log10(0.5 * np.array(evaluation_history)), \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"Mean Splitting Ratio (dB)\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can view the final spectral response to verify that the design performs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f0, dJ_du = opt([mapping(x, eta_i, cur_beta)], need_gradient=False)\n", - "frequencies = opt.frequencies\n", - "source_coef, top_coef, bottom_ceof = opt.get_objective_arguments()\n", - "\n", - "top_profile = np.abs(top_coef / source_coef) ** 2\n", - "bottom_profile = np.abs(bottom_ceof / source_coef) ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(1 / frequencies, top_profile * 100, \"-o\", label=\"Top Arm\")\n", - "plt.plot(1 / frequencies, bottom_profile * 100, \"--o\", label=\"Bottom Arm\")\n", - "plt.legend()\n", - "plt.grid(True)\n", - "plt.xlabel(\"Wavelength (microns)\")\n", - "plt.ylabel(\"Splitting Ratio (%)\")\n", - "plt.ylim(48.5, 50)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And of course we'll visualize the final topology. We'll plot the minimum length scale as a circle in the upper corner." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([mapping(x, eta_i, cur_beta)])\n", - "plt.figure()\n", - "ax = plt.gca()\n", - "opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - ")\n", - "circ = Circle((2, 2), minimum_length / 2)\n", - "ax.add_patch(circ)\n", - "ax.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/05-Near2Far-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/05-Near2Far-checkpoint.ipynb deleted file mode 100644 index 8b9ffc01e..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/05-Near2Far-checkpoint.ipynb +++ /dev/null @@ -1,459 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Near2Far Optimization with Epigraph Formulation\n", - "\n", - "The adjoint solver in meep now supports the adjoint simulation for near-to-far fields transformation. We present a simple optimization of metalens using the epigraph formulation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "from autograd import tensor_jacobian_product, grad\n", - "import nlopt\n", - "from matplotlib import pyplot as plt\n", - "from matplotlib.patches import Circle\n", - "from scipy import special, signal\n", - "\n", - "mp.verbosity(0)\n", - "Si = mp.Medium(index=3.4)\n", - "SiO2 = mp.Medium(index=1.44)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Basic setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "design_region_width = 15\n", - "design_region_height = 2\n", - "\n", - "pml_size = 1.0\n", - "\n", - "resolution = 20\n", - "\n", - "Sx = 2 * pml_size + design_region_width\n", - "Sy = 2 * pml_size + design_region_height + 5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "nf = 2\n", - "frequencies = np.array([1 / 1.5, 1 / 1.6])\n", - "\n", - "minimum_length = 0.09 # minimum length scale (microns)\n", - "eta_i = (\n", - " 0.5 # blueprint (or intermediate) design field thresholding point (between 0 and 1)\n", - ")\n", - "eta_e = 0.55 # erosion design field thresholding point (between 0 and 1)\n", - "eta_d = 1 - eta_e # dilation design field thresholding point (between 0 and 1)\n", - "filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)\n", - "design_region_resolution = int(resolution)\n", - "\n", - "pml_layers = [mp.PML(pml_size)]\n", - "\n", - "fcen = 1 / 1.55\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [0, -(design_region_height / 2 + 1.5), 0]\n", - "source_size = mp.Vector3(design_region_width, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "\n", - "Nx = int(design_region_resolution * design_region_width)\n", - "Ny = int(design_region_resolution * design_region_height)\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables,\n", - " volume=mp.Volume(\n", - " center=mp.Vector3(),\n", - " size=mp.Vector3(design_region_width, design_region_height, 0),\n", - " ),\n", - ")\n", - "\n", - "\n", - "def mapping(x, eta, beta):\n", - "\n", - " # filter\n", - " filtered_field = mpa.conic_filter(\n", - " x,\n", - " filter_radius,\n", - " design_region_width,\n", - " design_region_height,\n", - " design_region_resolution,\n", - " )\n", - "\n", - " # projection\n", - " projected_field = mpa.tanh_projection(filtered_field, beta, eta)\n", - "\n", - " projected_field = (\n", - " npa.flipud(projected_field) + projected_field\n", - " ) / 2 # left-right symmetry\n", - "\n", - " # interpolate to actual materials\n", - " return projected_field.flatten()\n", - "\n", - "\n", - "geometry = [\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ),\n", - " # mp.Block(center=design_region.center, size=design_region.size, material=design_variables, e1=mp.Vector3(x=-1))\n", - " #\n", - " # The commented lines above impose symmetry by overlapping design region with the same design variable. However,\n", - " # currently there is an issue of doing that; instead, we use an alternative approach to impose symmetry.\n", - " # See https://github.com/NanoComp/meep/issues/1984 and https://github.com/NanoComp/meep/issues/2093\n", - "]\n", - "kpoint = mp.Vector3()\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " k_point=kpoint,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=SiO2,\n", - " symmetries=[mp.Mirror(direction=mp.X)],\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will have two objective functions, one for each far point. However, we only need one `mpa.Near2FarFields` objective. We also need to provide a near-field monitor, from which the field at far point will be calculated. Only single monitor is supported right now, so the monitor needs to extend to the entire cell to capture all outgoing fields.\n", - "\n", - "When evaluated, mpa.Near2FarFields will return a numpy array with shape npoints by nfreq by 6 numpy array, where the third axis corresponds to the field components $E_x, E_y, E_z, H_x, H_y, H_z$, in that order. We will specify a objective as a function of the field components at frequencies of interest at points of interest. In this case, we would like to optimize $|E_z|^2$, and focus the fields of different frequency at different points." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "far_x = [mp.Vector3(0, 40, 0)]\n", - "NearRegions = [\n", - " mp.Near2FarRegion(\n", - " center=mp.Vector3(0, design_region_height / 2 + 1.5),\n", - " size=mp.Vector3(design_region_width, 0),\n", - " weight=+1,\n", - " )\n", - "]\n", - "FarFields = mpa.Near2FarFields(sim, NearRegions, far_x)\n", - "ob_list = [FarFields]\n", - "\n", - "\n", - "def J1(alpha):\n", - " return -npa.abs(alpha[0, :, 2]) ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=[J1],\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " frequencies=frequencies,\n", - ")\n", - "opt.plot2D(True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Our objective function that we pass to nlopt is rather simple. We'll introduce a dummy parameter `t`. The goal of the optimization problem will be to simply minimize the value of `t`. The gradient of this functional is rather straightforward." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "cur_iter = [0]\n", - "\n", - "\n", - "def f(x, grad):\n", - " t = x[0] # \"dummy\" parameter\n", - " v = x[1:] # design parameters\n", - " if grad.size > 0:\n", - " grad[0] = 1\n", - " grad[1:] = 0\n", - " return t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The key to the epigraph formulation (and most nonlinear optimization problems) lies in the constraints. We'll define one constraint for every frequency point of interest ($f_i$). We'll define our constraint as \n", - "\n", - "$$f_i < t$$\n", - "\n", - "where $t$ is the previosuly defined dummy parameter. Each constraint function is then defined by\n", - "\n", - "$$ c_i = f_i-t $$\n", - "\n", - "within some tolerance.\n", - "\n", - "More detail about this formulation can be found in the nlopt [documentation](https://nlopt.readthedocs.io/en/latest/NLopt_Introduction/#equivalent-formulations-of-optimization-problems)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def c(result, x, gradient, eta, beta):\n", - " print(\n", - " \"Current iteration: {}; current eta: {}, current beta: {}\".format(\n", - " cur_iter[0], eta, beta\n", - " )\n", - " )\n", - "\n", - " t = x[0] # dummy parameter\n", - " v = x[1:] # design parameters\n", - "\n", - " f0, dJ_du = opt([mapping(v, eta, beta)])\n", - "\n", - " # Backprop the gradients through our mapping function\n", - " my_grad = np.zeros(dJ_du.shape)\n", - " for k in range(opt.nf):\n", - " my_grad[:, k] = tensor_jacobian_product(mapping, 0)(v, eta, beta, dJ_du[:, k])\n", - "\n", - " # Assign gradients\n", - " if gradient.size > 0:\n", - " gradient[:, 0] = -1 # gradient w.r.t. \"t\"\n", - " gradient[:, 1:] = my_grad.T # gradient w.r.t. each frequency objective\n", - "\n", - " result[:] = np.real(f0) - t\n", - "\n", - " # store results\n", - " evaluation_history.append(np.real(f0))\n", - "\n", - " # visualize\n", - " plt.figure()\n", - " ax = plt.gca()\n", - " opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - " )\n", - " circ = Circle((2, 2), minimum_length / 2)\n", - " ax.add_patch(circ)\n", - " ax.axis(\"off\")\n", - " plt.show()\n", - "\n", - " cur_iter[0] = cur_iter[0] + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now run our optimizer in loop. The loop will increase beta and reset the optimizer, which is important since the cost function changes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny # number of parameters\n", - "\n", - "# Initial guess\n", - "x = np.ones((n,)) * 0.5\n", - "\n", - "# lower and upper bounds\n", - "lb = np.zeros((Nx * Ny,))\n", - "ub = np.ones((Nx * Ny,))\n", - "\n", - "# insert dummy parameter bounds and variable\n", - "x = np.insert(x, 0, 0) # our initial guess for the worst error\n", - "lb = np.insert(lb, 0, -np.inf)\n", - "ub = np.insert(ub, 0, 0)\n", - "\n", - "cur_beta = 4\n", - "beta_scale = 2\n", - "num_betas = 6\n", - "update_factor = 12\n", - "ftol = 1e-5\n", - "for iters in range(num_betas):\n", - " solver = nlopt.opt(algorithm, n + 1)\n", - " solver.set_lower_bounds(lb)\n", - " solver.set_upper_bounds(ub)\n", - " solver.set_min_objective(f)\n", - " solver.set_maxeval(update_factor)\n", - " solver.set_ftol_rel(ftol)\n", - " solver.add_inequality_mconstraint(\n", - " lambda r, x, g: c(r, x, g, eta_i, cur_beta), np.array([1e-3] * nf)\n", - " )\n", - " x[:] = solver.optimize(x)\n", - " cur_beta = cur_beta * beta_scale" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "lb = -np.min(evaluation_history, axis=1)\n", - "ub = -np.max(evaluation_history, axis=1)\n", - "mean = -np.mean(evaluation_history, axis=1)\n", - "\n", - "num_iters = lb.size\n", - "\n", - "plt.figure()\n", - "plt.fill_between(np.arange(num_iters), ub, lb, alpha=0.3)\n", - "plt.plot(mean, \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"FOM\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot our results and see the resulting geometry." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([mapping(x[1:], eta_i, cur_beta)])\n", - "plt.figure()\n", - "ax = plt.gca()\n", - "opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - ")\n", - "circ = Circle((2, 2), minimum_length / 2)\n", - "ax.add_patch(circ)\n", - "ax.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To check the performance of our final structure, we use a CW source at the desired frequency and plot the fields after the struture." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.sim = mp.Simulation(\n", - " cell_size=mp.Vector3(Sx, 90),\n", - " boundary_layers=pml_layers,\n", - " k_point=kpoint,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=SiO2,\n", - " resolution=resolution,\n", - ")\n", - "src = mp.ContinuousSource(frequency=1 / 1.5, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "opt.sim.change_sources(source)\n", - "\n", - "opt.sim.run(until=200)\n", - "plt.figure(figsize=(10, 20))\n", - "opt.sim.plot2D(fields=mp.Ez)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.sim = mp.Simulation(\n", - " cell_size=mp.Vector3(Sx, 90),\n", - " boundary_layers=pml_layers,\n", - " k_point=kpoint,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=SiO2,\n", - " resolution=resolution,\n", - ")\n", - "src = mp.ContinuousSource(frequency=1 / 1.6, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "opt.sim.change_sources(source)\n", - "\n", - "opt.sim.run(until=200)\n", - "plt.figure(figsize=(10, 20))\n", - "opt.sim.plot2D(fields=mp.Ez)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/06-Near2Far-Epigraph-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/06-Near2Far-Epigraph-checkpoint.ipynb deleted file mode 100644 index 407781c03..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/06-Near2Far-Epigraph-checkpoint.ipynb +++ /dev/null @@ -1,519 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Near2Far Optimization with Epigraph Formulation\n", - "\n", - "This tutorial redoes the previous example with epigraph formulation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "from autograd import tensor_jacobian_product, grad\n", - "import nlopt\n", - "from matplotlib import pyplot as plt\n", - "from matplotlib.patches import Circle\n", - "from scipy import special, signal\n", - "\n", - "mp.verbosity(0)\n", - "Si = mp.Medium(index=3.4)\n", - "Air = mp.Medium(index=1.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Basic setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "design_region_width = 15\n", - "design_region_height = 2\n", - "\n", - "pml_size = 1.0\n", - "\n", - "resolution = 30\n", - "\n", - "Sx = 2 * pml_size + design_region_width\n", - "Sy = 2 * pml_size + design_region_height + 5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "nf = 3\n", - "frequencies = np.array([1 / 1.5, 1 / 1.55, 1 / 1.6])\n", - "\n", - "minimum_length = 0.09 # minimum length scale (microns)\n", - "eta_i = (\n", - " 0.5 # blueprint (or intermediate) design field thresholding point (between 0 and 1)\n", - ")\n", - "eta_e = 0.55 # erosion design field thresholding point (between 0 and 1)\n", - "eta_d = 1 - eta_e # dilation design field thresholding point (between 0 and 1)\n", - "filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)\n", - "design_region_resolution = int(resolution)\n", - "\n", - "pml_layers = [mp.PML(pml_size)]\n", - "\n", - "fcen = 1 / 1.55\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [0, -(design_region_height / 2 + 1.5), 0]\n", - "source_size = mp.Vector3(design_region_width, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "\n", - "Nx = int(design_region_resolution * design_region_width)\n", - "Ny = int(design_region_resolution * design_region_height)\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), Air, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables,\n", - " volume=mp.Volume(\n", - " center=mp.Vector3(),\n", - " size=mp.Vector3(design_region_width, design_region_height, 0),\n", - " ),\n", - ")\n", - "\n", - "\n", - "def mapping(x, eta, beta):\n", - "\n", - " # filter\n", - " filtered_field = mpa.conic_filter(\n", - " x,\n", - " filter_radius,\n", - " design_region_width,\n", - " design_region_height,\n", - " design_region_resolution,\n", - " )\n", - "\n", - " # projection\n", - " projected_field = mpa.tanh_projection(filtered_field, beta, eta)\n", - "\n", - " projected_field = (\n", - " npa.flipud(projected_field) + projected_field\n", - " ) / 2 # left-right symmetry\n", - "\n", - " # interpolate to actual materials\n", - " return projected_field.flatten()\n", - "\n", - "\n", - "geometry = [\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ),\n", - " # mp.Block(center=design_region.center, size=design_region.size, material=design_variables, e1=mp.Vector3(x=-1))\n", - " #\n", - " # The commented lines above impose symmetry by overlapping design region with the same design variable. However,\n", - " # currently there is an issue of doing that; instead, we use an alternative approach to impose symmetry.\n", - " # See https://github.com/NanoComp/meep/issues/1984 and https://github.com/NanoComp/meep/issues/2093\n", - "]\n", - "kpoint = mp.Vector3()\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=Air,\n", - " symmetries=[mp.Mirror(direction=mp.X)],\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of the calculating the average of intensities, we change our objective function to be vector-valued intensities at the frequencies of interest.\n", - "\n", - "Note that because ``meep.adjoint`` looks at the fields in the frequency domain (i.e. Fourier transformed fields), the ``objective_functions`` from ``meep.adjoint`` can be vector-valued if and only if each component of the vector is at different frequencies.\n", - "\n", - "Also note that this objective function is different from the objective for `nlopt`, which has to be a scalar. In our case, this is a dummpy variable `t` from the epigraph formulation, introduced lated in the tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "far_x = [mp.Vector3(0, 15, 0)]\n", - "NearRegions = [\n", - " mp.Near2FarRegion(\n", - " center=mp.Vector3(0, design_region_height / 2 + 1.5),\n", - " size=mp.Vector3(design_region_width, 0),\n", - " weight=+1,\n", - " )\n", - "]\n", - "FarFields = mpa.Near2FarFields(sim, NearRegions, far_x)\n", - "ob_list = [FarFields]\n", - "\n", - "\n", - "def J1(FF):\n", - " return -npa.abs(FF[0, :, 2]) ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=[J1],\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " frequencies=frequencies,\n", - " maximum_run_time=2000,\n", - ")\n", - "opt.plot2D(True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Our objective function that we pass to nlopt is rather simple. We'll introduce a dummy parameter `t`. The goal of the optimization problem will be to simply minimize the value of `t`. The gradient of this functional is rather straightforward." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "cur_iter = [0]\n", - "\n", - "\n", - "def f(x, grad):\n", - " t = x[0] # \"dummy\" parameter\n", - " v = x[1:] # design parameters\n", - " if grad.size > 0:\n", - " grad[0] = 1\n", - " grad[1:] = 0\n", - " return t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The key to the epigraph formulation (and most nonlinear optimization problems) lies in the constraints. We'll define one constraint for every frequency point of interest ($f_i$). We'll define our constraint as \n", - "\n", - "$$f_i < t$$\n", - "\n", - "where $t$ is the previosuly defined dummy parameter. Each constraint function is then defined by\n", - "\n", - "$$ c_i = f_i-t $$\n", - "\n", - "within some tolerance.\n", - "\n", - "More detail about this formulation can be found in the nlopt [documentation](https://nlopt.readthedocs.io/en/latest/NLopt_Introduction/#equivalent-formulations-of-optimization-problems)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def c(result, x, gradient, eta, beta):\n", - " print(\n", - " \"Current iteration: {}; current eta: {}, current beta: {}\".format(\n", - " cur_iter[0], eta, beta\n", - " )\n", - " )\n", - "\n", - " t = x[0] # dummy parameter\n", - " v = x[1:] # design parameters\n", - "\n", - " f0, dJ_du = opt([mapping(v, eta, beta)])\n", - "\n", - " # Backprop the gradients through our mapping function\n", - " my_grad = np.zeros(dJ_du.shape)\n", - " for k in range(opt.nf):\n", - " my_grad[:, k] = tensor_jacobian_product(mapping, 0)(v, eta, beta, dJ_du[:, k])\n", - " # Note that we now backpropogate the gradients at individual frequencies\n", - "\n", - " # Assign gradients\n", - " if gradient.size > 0:\n", - " gradient[:, 0] = -1 # gradient w.r.t. \"t\"\n", - " gradient[:, 1:] = my_grad.T # gradient w.r.t. each frequency objective\n", - "\n", - " result[:] = np.real(f0) - t\n", - "\n", - " # store results\n", - " evaluation_history.append(np.real(f0))\n", - "\n", - " # visualize\n", - " plt.figure()\n", - " ax = plt.gca()\n", - " opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - " )\n", - " circ = Circle((2, 2), minimum_length / 2)\n", - " ax.add_patch(circ)\n", - " ax.axis(\"off\")\n", - " plt.show()\n", - "\n", - " cur_iter[0] = cur_iter[0] + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now run our optimizer in loop. The loop will increase beta and reset the optimizer, which is important since the cost function changes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny # number of parameters\n", - "\n", - "# Initial guess\n", - "x = np.ones((n,)) * 0.5\n", - "\n", - "# lower and upper bounds\n", - "lb = np.zeros((Nx * Ny,))\n", - "ub = np.ones((Nx * Ny,))\n", - "\n", - "# insert dummy parameter bounds and variable\n", - "x = np.insert(x, 0, -1) # our initial guess for the worst error\n", - "lb = np.insert(lb, 0, -np.inf)\n", - "ub = np.insert(ub, 0, 0)\n", - "\n", - "cur_beta = 4\n", - "beta_scale = 2\n", - "num_betas = 6\n", - "update_factor = 12\n", - "ftol = 1e-5\n", - "for iters in range(num_betas):\n", - " solver = nlopt.opt(algorithm, n + 1)\n", - " solver.set_lower_bounds(lb)\n", - " solver.set_upper_bounds(ub)\n", - " solver.set_min_objective(f)\n", - " solver.set_maxeval(update_factor)\n", - " solver.set_ftol_rel(ftol)\n", - " solver.add_inequality_mconstraint(\n", - " lambda r, x, g: c(r, x, g, eta_i, cur_beta), np.array([1e-3] * nf)\n", - " )\n", - " x[:] = solver.optimize(x)\n", - " cur_beta = cur_beta * beta_scale" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "lb = -np.min(evaluation_history, axis=1)\n", - "ub = -np.max(evaluation_history, axis=1)\n", - "mean = -np.mean(evaluation_history, axis=1)\n", - "\n", - "num_iters = lb.size\n", - "\n", - "plt.figure()\n", - "plt.fill_between(np.arange(num_iters), ub, lb, alpha=0.3)\n", - "plt.plot(mean, \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"FOM\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot our results and see the resulting geometry." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([mapping(x[1:], eta_i, cur_beta)])\n", - "plt.figure()\n", - "ax = plt.gca()\n", - "opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - ")\n", - "circ = Circle((2, 2), minimum_length / 2)\n", - "ax.add_patch(circ)\n", - "ax.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To check the performance of our final structure, we plot $|𝐸_𝑧|_2 $ at those three frequencies again. The enhancement is universal across all frequencies now." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f0, dJ_du = opt([mapping(x[1:], eta_i, cur_beta // 2)], need_gradient=False)\n", - "frequencies = opt.frequencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "intensities = np.abs(opt.get_objective_arguments()[0][0, :, 2]) ** 2\n", - "\n", - "plt.figure()\n", - "plt.plot(1 / frequencies, intensities, \"-o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Wavelength (microns)\")\n", - "plt.ylabel(\"|Ez|^2 Intensities\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also use a CW source at individual frequencies and plot the fields." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.sim = mp.Simulation(\n", - " cell_size=mp.Vector3(Sx, 40),\n", - " boundary_layers=pml_layers,\n", - " k_point=kpoint,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=Air,\n", - " resolution=resolution,\n", - ")\n", - "src = mp.ContinuousSource(frequency=1 / 1.5, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "opt.sim.change_sources(source)\n", - "\n", - "opt.sim.run(until=200)\n", - "plt.figure(figsize=(10, 20))\n", - "opt.sim.plot2D(fields=mp.Ez)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.sim = mp.Simulation(\n", - " cell_size=mp.Vector3(Sx, 40),\n", - " boundary_layers=pml_layers,\n", - " k_point=kpoint,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=Air,\n", - " resolution=resolution,\n", - ")\n", - "src = mp.ContinuousSource(frequency=1 / 1.55, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "opt.sim.change_sources(source)\n", - "\n", - "opt.sim.run(until=200)\n", - "plt.figure(figsize=(10, 20))\n", - "opt.sim.plot2D(fields=mp.Ez)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.sim = mp.Simulation(\n", - " cell_size=mp.Vector3(Sx, 40),\n", - " boundary_layers=pml_layers,\n", - " k_point=kpoint,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=Air,\n", - " resolution=resolution,\n", - ")\n", - "src = mp.ContinuousSource(frequency=1 / 1.6, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "opt.sim.change_sources(source)\n", - "\n", - "opt.sim.run(until=200)\n", - "plt.figure(figsize=(10, 20))\n", - "opt.sim.plot2D(fields=mp.Ez)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/07-Connectivity-Constraint-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/07-Connectivity-Constraint-checkpoint.ipynb deleted file mode 100644 index 2cc9802c9..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/07-Connectivity-Constraint-checkpoint.ipynb +++ /dev/null @@ -1,123938 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "4b5849aa", - "metadata": {}, - "source": [ - "# Connectivity Constraint " - ] - }, - { - "cell_type": "markdown", - "id": "890e58ab", - "metadata": {}, - "source": [ - "For manufacturability, it is often necessary to impose connectivity constraint during topological optimization, so that all solid pixels are connected to one side (e.g. the supporting substrate). Meep adjoint supports this feature and it is illustrated in this tutorial. Please also note that the connectivity constraint is independent from the rest of Meep, and may be used independently and separately.\n", - "\n", - "The basic underlying idea is to solve for an auxilary artificial temperature field. For connected structure, heat can freely flow out through the supporing layer, so the temperature in the region should be low overall; on the other hand, for disconnected structure, heat will be trapped in islands, leading to high temperature in some area. Reference: Li, Q., Chen, W., Liu, S. et al. \"Structural topology optimization considering connectivity constraint\". Struct Multidisc Optim 54, 971–984 (2016). https://doi.org/10.1007/s00158-016-1459-5" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a6964ee3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Using MPI version 3.1, 1 processes\n" - ] - } - ], - "source": [ - "import meep as mp\n", - "from meep import Animate2D\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "from autograd import tensor_jacobian_product, grad\n", - "import nlopt\n", - "from matplotlib import pyplot as plt\n", - "from matplotlib.patches import Circle\n", - "from scipy import special, signal\n", - "\n", - "mp.verbosity(0)\n", - "Si = mp.Medium(index=3.4)\n", - "Air = mp.Medium(index=1.0)" - ] - }, - { - "cell_type": "markdown", - "id": "3dc1f9ab", - "metadata": {}, - "source": [ - "# I. How the Connectivity Constraint Works" - ] - }, - { - "cell_type": "markdown", - "id": "4210a705", - "metadata": {}, - "source": [ - "Given (filtered and projected) design variable `v` with size `(nz, ny, nx)`, `mpa.constraint_connectivity(v, nx, ny, nz)` computes and simultaneously outputs the constraint value (negative for connected structure, positive for disconnected structure) and the gradient of the constraint with respect to `v`. Additional chain rule needs to be applied for gradient with repect to the unprojected and unfiltered design variables.\n", - "\n", - "The temperature field is also returned for debug and reference, but it can be ignored when using the constraint.\n", - "#### IMPORTANT: The solver will reshape input `v` via `np.reshape(v, (nz, ny, nx))`. For 2D structure, please specify `ny=1`. The solver assumes the last index of the 0-axis of the array (bottom side of the image from plt.imshow) is where the structure should be connected. So the user must first rotate the structure properly to match this condition; the gradient must also be post-processed and rotated so that the gradient corresponds correctly.\n", - "\n", - "There are three important hyperparameter, `p`, `cond_s`, and `thresh`. It is advised to check and tune them for each individual problem setup so that the constraint behaves as expected. A tall design region might require larger `cond_s` and `thresh`, as the heat is more difficult to flow out. On the other hand, there are many good combinations, and a reasonably conservative `thresh` might be all one needs to tune. " - ] - }, - { - "cell_type": "markdown", - "id": "d86eeb17", - "metadata": {}, - "source": [ - "### disconnected structure" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7299cb1d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "nz, ny, nx = 100, 1, 50\n", - "v = np.zeros((nz,ny,nx))\n", - "v[30:65,:,20:30]=1\n", - "v[70:,:,20:30]=1\n", - "plt.imshow(v[:,0,:])\n", - "plt.colorbar()" - ] - }, - { - "cell_type": "markdown", - "id": "2ad6e653", - "metadata": {}, - "source": [ - "The structure above has one part that is connected to the bottom, while the other part is floating above and is disconnected. We can see overall has a positive constraint value of around 24" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "8e8f0e8d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "24.234376324325485" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T, f, df_dv = mpa.constraint_connectivity(v, nx, ny, nz, p=3, cond_s=1e4, thresh=50)\n", - "f" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "df183225", - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.imshow(T.reshape(nz, nx))\n", - "plt.colorbar()" - ] - }, - { - "cell_type": "markdown", - "id": "e7367fc3", - "metadata": {}, - "source": [ - "The temperature in the region is plotted above. As we can see, only the top disconnected part is heated up, while the bottom connected part is cooled down. We also plotted the gradient below, which has large negative values between the two components, indicating strong tendency to connect the structure in order to lower the temperature." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "fb8073ec", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.imshow(df_dv.reshape(nz, nx))\n", - "plt.colorbar()" - ] - }, - { - "cell_type": "markdown", - "id": "2b886600", - "metadata": {}, - "source": [ - "### connected structure" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "ee852785", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "v[30:,:,40]=1\n", - "v[40,:,30:40]=1\n", - "plt.imshow(v[:,0,:])\n", - "plt.colorbar()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "6fd7737b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-0.2505643580057533" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T, f, df_dv = mpa.constraint_connectivity(v, nx, ny, nz, p=3, cond_s=1e4, thresh=50)\n", - "f" - ] - }, - { - "cell_type": "markdown", - "id": "c02f16f7", - "metadata": {}, - "source": [ - "If we connect the structure so that all solid pixel is path-connected to the bottom, we can see that the constraint value becomes negative, and the tempeature is low overall as heat is leaked through the bottom side. The gradient values are relatively small in generall as well, indicating no more change is needed to connect the structure." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "50dd1f7f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.imshow(T.reshape(nz, nx))\n", - "plt.colorbar()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "df47301c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plt.imshow(df_dv.reshape(nz, nx))\n", - "plt.colorbar()" - ] - }, - { - "cell_type": "markdown", - "id": "119e6ba2", - "metadata": {}, - "source": [ - "# II. Optimization Example\n", - "\n", - "We further illustrates the constraint with the following topological optimization example. A simple metalens sitting on a substrate is designed to focus light. Length-scale and connectivity constraints are both imposed to ensure manufactuability. Length-scale is imposed so that components are connected by more than a narrow path of a few pixels, and eliminate small islands that don't signicantly violates the connectivity constraint.\n", - "\n", - "In this example, we not only enforce solid pixels to connect to the substrate, we also enforce the void pixels to connect to the air above. Therefore, the structure has no holes and can be easily cleaned with alcohol." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "e838e472", - "metadata": {}, - "outputs": [], - "source": [ - "pad = 0.1\n", - "design_region_width0 = 8\n", - "design_region_height0 = 2\n", - "design_region_width = design_region_width0 + 2*pad\n", - "design_region_height = design_region_height0 + 2*pad\n", - "\n", - "pml_size = 1.0\n", - "\n", - "resolution = 30\n", - "\n", - "Sx = 2 * pml_size + design_region_width\n", - "Sy = 2 * pml_size + design_region_height + 3\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "nf = 1\n", - "frequencies = np.array([1 / 1.55])\n", - "\n", - "minimum_length = 0.15 \n", - "eta_i = 0.5\n", - "eta_e = 0.75\n", - "eta_d = 1 - eta_e\n", - "filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)\n", - "design_region_resolution = int(resolution)\n", - "\n", - "pml_layers = [mp.PML(pml_size)]\n", - "\n", - "\n", - "\n", - "fcen = 1 / 1.55\n", - "width = 0.1\n", - "fwidth = width * fcen\n", - "source_center = [0, -(design_region_height / 2 + 1), 0]\n", - "source_size = mp.Vector3(design_region_width, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [mp.Source(src, component=mp.Ez, size=source_size, center=source_center)]\n", - "\n", - "Nx = round(design_region_resolution * design_region_width)\n", - "Ny = round(design_region_resolution * design_region_height)\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), Air, Si,damping=0.2*6.28*fcen)\n", - "design_region = mpa.DesignRegion(\n", - " design_variables,\n", - " volume=mp.Volume(\n", - " center=mp.Vector3(),\n", - " size=mp.Vector3(design_region_width, design_region_height, 0),\n", - " ),\n", - ")\n", - "\n", - "\n", - "# Padding and masking the design region to ensure geometric constraints on the boundaries.\n", - "mask = pad\n", - "x_g = np.linspace(-design_region_width / 2, design_region_width / 2, Nx)\n", - "y_g = np.linspace(-design_region_height / 2, design_region_height / 2, Ny)\n", - "X_g, Y_g = np.meshgrid(x_g, y_g, sparse=True, indexing=\"ij\")\n", - "left_mask = (X_g <= -(design_region_width/2-mask)) & (np.abs(Y_g) <= design_region_height / 2)\n", - "right_mask = (X_g >= design_region_width/2-mask) & (np.abs(Y_g) <= design_region_height / 2)\n", - "top_mask = (Y_g >= design_region_height/2-mask) & (np.abs(X_g) <= design_region_width / 2)\n", - "bottom_mask = (Y_g <= -(design_region_height/2-mask)) & (np.abs(X_g) <= design_region_width / 2)\n", - "Air_mask = left_mask | top_mask | right_mask\n", - "\n", - "def mapping(x,eta,beta): \n", - " xc = npa.where(bottom_mask.flatten(),1,npa.where(Air_mask.flatten(),0,x))\n", - " xc = npa.reshape(xc, (Nx, Ny)) \n", - "\n", - " # filter \n", - " filtered_field = mpa.conic_filter(xc,filter_radius,design_region_width,design_region_height, design_region_resolution) \n", - " return filtered_field.flatten()\n", - "\n", - "\n", - "# ``mapping_s`` and ``mapping_v`` are very important functions: they pre-process the design variables by masking, \n", - "# filtering, projecting, and rotating to the proper orientation. The mapping function above is for the Maxwell solves, \n", - "# and has no projection because that is taken care of as part of meep routine, using beta defined in the design variable. \n", - "# On the other hand, for connectivity constraint, projection must be separatly specified. Afterwards, \n", - "# ``rot90`` in ``mapping_s`` would rotate the meep design variables properly for the constraint, \n", - "# so that connectivity of solid to the bottom is enforced. Similarly, in ``mapping_v``,\n", - "# ``1 - npa.rot90(projected_field,3)`` are for the void pixels to connect to the air above.\n", - "# Packing the preprocessing functions this way allows for easy post-processsing of the gradients with chain-rule.\n", - "\n", - "def mapping_s(x,eta,beta):\n", - " # filter\n", - " xc = npa.where(bottom_mask.flatten(),1,npa.where(Air_mask.flatten(),0,x))\n", - " xc = npa.reshape(xc, (Nx, Ny))\n", - " filtered_field = mpa.conic_filter(x,filter_radius,design_region_width, design_region_height, design_region_resolution)\n", - " # projection \n", - " projected_field = mpa.tanh_projection(filtered_field,beta,eta)\n", - "\n", - " rot_field = npa.rot90(projected_field)\n", - " return rot_field.flatten()\n", - "\n", - "def mapping_v(x,eta,beta):\n", - " # filter \n", - " xc = npa.where(bottom_mask.flatten(),1,npa.where(Air_mask.flatten(),0,x))\n", - " xc = npa.reshape(xc, (Nx, Ny))\n", - " filtered_field = mpa.conic_filter(x,filter_radius,design_region_width, design_region_height, design_region_resolution)\n", - " # projection \n", - " projected_field = mpa.tanh_projection(filtered_field,beta,eta)\n", - "\n", - " rot_field = 1 - npa.rot90(projected_field, 3)\n", - " return rot_field.flatten()\n", - "\n", - "\n", - "geometry = [\n", - " mp.Block(center=design_region.center, size=design_region.size, material=design_variables),\n", - " mp.Block(center=mp.Vector3(0, -design_region_height0 / 2 - 0.25), size=mp.Vector3(design_region_width, 0.5), material=Si),\n", - "]\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=Air,\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "dbaa2395", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "far_x = [mp.Vector3(0, 5, 0)]\n", - "NearRegions = [\n", - " mp.Near2FarRegion(\n", - " center=mp.Vector3(0, design_region_height / 2 + 0.5),\n", - " size=mp.Vector3(design_region_width, 0),\n", - " weight=+1,\n", - " )\n", - "]\n", - "FarFields = mpa.Near2FarFields(sim, NearRegions, far_x)\n", - "ob_list = [FarFields]\n", - "\n", - "def J(FF):\n", - " return npa.abs(FF[0, 0, 2]) ** 2\n", - "\n", - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=[J],\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " frequencies=frequencies,\n", - " maximum_run_time=500,\n", - ")\n", - "opt.plot2D(True)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "229cfe3f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Current iteration: 1\n", - "Starting forward run...\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3ebed0792ef644a0b02d536ad6da6741", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAtTklEQVR4nO3de5CdZX0H8N/Z3WRzIbsIDVAmG7nYsaBCWm5GHQ2aCkynlTql/mHLZRjG0sDIxLaQXqDM6MQZmNYpUGAYBKeV0RZBaltUqiVMR5HIRRENnYhATASSYHZDSDbJ7vYP3XWznMt7ru97zvP5zOwke/Y95/ze+/c87/O8pzQ1NTUVAAAkoy/vAgAA6CwBEAAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkBgBEAAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkBgBEAAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJCYgbwL6GaTk5Oxbdu2WLJkSZRKpbzLAQAymJqait27d8exxx4bfX1ptoUJgE3Ytm1bjIyM5F0GANCALVu2xLJly/IuIxcCYBOWLFkSEb/YgIaGhnKuBgDIYmxsLEZGRmbO4ykSAJswfdl3aGhIAASALpNy9600L3wDACRMAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkBgBEAAgMQIgAEBiBEAAgMQkHQBvvfXWOOWUU2JoaCiGhoZi5cqV8eCDD+ZdFgBAWyUdAJctWxaf/vSn4/HHH4/vfve78f73vz8+9KEPxTPPPJN3aQAAbVOampqayruIIjniiCPihhtuiEsvvbTmtGNjYzE8PByjo6MxNDTUgeoAgGY5f0cM5F1AUUxMTMS//du/xZ49e2LlypV5lwMA0DbJB8Cnn346Vq5cGfv27YvDDjss7r///jj55JPLTjs+Ph7j4+Mzv4+NjXWqTACAlkm6D2BExFvf+tZ46qmn4jvf+U5cfvnlcdFFF8UPf/jDstOuX78+hoeHZ35GRkY6XC0AQPP0AZxj9erVceKJJ8btt9/+hr+VawEcGRlJug8BAHQbfQBdAn6DycnJQ0LebIODgzE4ONjhigAAWivpALhu3bo477zzYvny5bF79+6455574uGHH46vfe1reZcGANA2SQfAV155JS688ML42c9+FsPDw3HKKafE1772tfid3/mdvEsDAGibpAPgnXfemXcJAAAdl/woYACA1AiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkBgBEAAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkJikA+D69evjjDPOiCVLlsRRRx0V559/fjz77LN5lwUA0FZJB8ANGzbEmjVr4tFHH42HHnooDhw4EB/84Adjz549eZcGANA2pampqam8iyiK7du3x1FHHRUbNmyI9773vTWnHxsbi+Hh4RgdHY2hoaEOVAgANMv5O2Ig7wKKZHR0NCIijjjiiLJ/Hx8fj/Hx8Znfx8bGOlIXAEArJX0JeLbJycm46qqr4t3vfne8/e1vLzvN+vXrY3h4eOZnZGSkw1UCADTPJeBfuvzyy+PBBx+M//3f/41ly5aVnaZcC+DIyEjSTcgA0G1cAnYJOCIirrjiiviP//iPeOSRRyqGv4iIwcHBGBwc7GBlAACtl3QAnJqaiiuvvDLuv//+ePjhh+P444/PuyQAgLZLOgCuWbMm7rnnnnjggQdiyZIl8dJLL0VExPDwcCxcuDDn6gAA2iPpPoClUqns43fddVdcfPHFNZ+vDwEAdB/n78RbABPOvgBAwtwGBgAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkBgBEAAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABKTdAB85JFH4vd+7/fi2GOPjVKpFF/+8pfzLgkAoO2SDoB79uyJU089NW655Za8SwEA6JiBvAvI03nnnRfnnXde3mUAAHRU0gGQ2qampg75F4DeViqVDvmX3iQA1mF8fDzGx8dnfh8bG8uxmvabmpqKqampmJycnPm/IAjQm0ql0sxPX1/fzGP0JgGwDuvXr4/rr78+7zI6Yjr4TU5OxsGDB+PgwYMxMTExEwYB6B3Toa+/vz8GBgZiYGAg+vr6oq+vTwjsUQJgHdatWxdr166d+X1sbCxGRkZyrKg9Zrf87d+/P8bHx2Pv3r0xPj4eBw4ciMnJybxLBKCF+vr6Yt68eTE4OBgLFy6MwcHBmD9/vsvBPUwArMPg4GAMDg7mXUZHTE1NxcTERIyPj8fu3btjdHQ0xsbGYv/+/TExMZF3eQC0UH9/f8yfPz+GhoZieHh45jEtgL0r6QD42muvxebNm2d+/8lPfhJPPfVUHHHEEbF8+fIcK8vf9KXfffv2xdjYWOzYsSN+/vOfx969e+PgwYN5lwdACw0MDMTChQtj//79USqVYv78+TF//vwYGEg6JvS0pNfsd7/73Tj77LNnfp++vHvRRRfF3XffnVNV+Zvu4zd9CXjPnj2xa9euePXVV+P1118XAAF6zMDAQCxatCj6+vpi0aJFsWTJkli8ePFMlyCtgL0n6QC4atUqAxoqmO4DePDgwRgfH499+/bF3r17tQAC9KCBgYEolUqxd+/e2L9/fxw8eNCgvx6XdACkutmDQSYnJ2NiYmJmNDAAvWX6Tg+CXxqS/io4snMwAEiD430aBEAAgMQIgADADAM+0qAPIE1r5eWCUqmU1OWHVs5vtyy76ZNLN9Taau1eR6ks22aWY7fsJ3O1uu5qIa8blw/1EwAplNQOPK2c325Zdt1SZ6u16gReLeRNP9atISerZuatW5dLt9ZNcbkEDF1m+sva6S6dDPvCwq/YV6A8ARC6zPTtefiVlE7y1j3QCgIgXUGrF9UIRWmpdCwo9/jcbcNxBH5BAKRrdOtJ3gkHWqvSsSDLMaIox5F6jwuOI7SaAEhXKMpBu50c4CGbFPeVeo6BKS4f6icAQptlPXCnEHKhFXphX2nnPPTC8qH9BEAA6EJa+miGAEjhpHZQS21+G2U5pc36fyMtfTRDAASg8Gbf5BpongBI4fTqp9pyJ67px6qd1LKe8Hr9xFhtu+iWeW9VnVlep1uWyVzN1t1N820kMHnyVXDkqtZ3l5b7ezd+zVW18Bfxi/mbO1+1DvbdeDKodp+2Suu11nLptm0hq+n5LredlJu2m82tf+68z239q3RcyPoe3bDNVFrvlZZJuWWRZX8iXVoAaZlGTkK1vtWi2vedNvO+nTL7BtazD9jlTnhz/z89/dwDe63QXFS11tPc5TN7Xuf+Pvd1a7UO5rWNlFuvlf4+V6WTd7m/FX1bqLYOsiyDetZhN+wv03VVWseVQl4llYJwpWANEQIgLVTrYNZKWT7N53HSz/KeWb/Krd6Ddd4H9lon+Dzry3vZVKuhnpN7K6bLQ60PedX2iVrzVS1MdbtGrgLU+4GadLkETFu14oCTpRWwXe9dr1Zcqi7X8lfu9ad/L8rJL8sJvNwlzdnKXe6tdNkz67wXKXg22nJX6zJ5JUXZNiKaWw/lWsKq7SeV3rOIyyLrOip3SXzu/6s9BnMJgNAmrToI1zpBdNvBvpETdrUQ2KsaCW/duDxmz2fWMF+pZawb57+eDzBFDbR0JwEQctbs7S2K1MrTTinM42yptOx0ep6Kvr+0u7aizz+dow8g5KRVI5u77WDeTNCFiO7b5qGIBEBazom6vFqDJFK5/1kzJ+9umcci6OVl1cy8FSk8Vjsm5Dl6nTQIgLRckQ6wRZFlpGejoyDrna5IWnFrlJQIBJRTz3aR+j7ErwiAtFyeJ6miniDLBby59+tq5D5p3a6efm69eOKqd912022Bsurl7buWavd6bPZ1oBYBkJZzMGqtXlue7T7hd1OgqHfdZrks2KouBXnJWne3zl89em3fp1gEQDqunQfuIh4wUzhR1Su1baCWeoJdLVlvqdJJeVzWL/J+V+sbYlr5+lCJAEjHFenE1AmNfJ1TNVkO7kU+AUxfDm9VjUWe16yyfDtMI62FRdHqfT7r8irqQIqsN/ZutPbUjrE0RgCkJxTxID9Xqw7KWb5SrBtOAO1eHr2qlwcFRWSrO2t/2aIvg0p9YIvYikvvEQAhZ1m+6xQiBIJaeunm2d1aN91DAIScdXun/TxZZpVZNpUVfdm089J10eedzhEA6Rgd/9srxWXQbfPcyZNvty2b2dq9nIq8bFo1QMTNpKlFAKRjGj3opn4AK/LJqoiKur1o6a1tOrBkGRiRynJsZiBIucEyjidMEwApPAcsIuo7ERY5HLRytHc7n98q9X5LRV77e1GWVzXdUCPdQwCkZRycaKduH/1ab7hpNigWZTlkGeRUhHvhFWV5lePYSjsM5F0AvaPIB9Ciq3XJy7KtT0rLq9vntd5tv9s/CGRV7l6Z3T5PFIsWQCi4Tl0y7BWWQ+/Ics/LLI93K/cDpJ1yC4Dbtm3L660Pccstt8Rxxx0XCxYsiLPOOisee+yxvEvqee3o29TtB/5q9ffa/DY7yrFX5p3KWvkNGN26zKfrFv5ol9wC4Nve9ra455578nr7iIj44he/GGvXro3rrrsunnjiiTj11FPjnHPOiVdeeSXXunpd1jv9V+ob1MlLI538urJq81vutWYvo247yc2uuZG+WtXmvcgnzE7U1o3bw2xZa69nPudO183LJ6L766cYcguAn/rUp+JjH/tYXHDBBfHqq6/mUsPf//3fx2WXXRaXXHJJnHzyyXHbbbfFokWL4rOf/Wwu9VCfSieAVoaiVnz/aqMteL3W8teoueuy1nLptWXU6OjnoiyHrPtiPdNl/Vul5VHEDwnl5r/csWzuB0BoVG4B8M/+7M/i+9//fuzcuTNOPvnk+MpXvtLR99+/f388/vjjsXr16pnH+vr6YvXq1fHtb3+77HPGx8djbGzskB9aL8tBsNr0c+9/lefNd5u5xDm3paue52Z9/244gTQyeraI89XKy5rdqN3rpJ4W5SJuH1m/ErLaMaZbrwiQj1xHAR9//PHxzW9+M26++eb48Ic/HCeddFIMDBxa0hNPPNGW996xY0dMTEzE0UcffcjjRx99dGzatKnsc9avXx/XX399W+rpBeU+Yc8exTf9/7mPNfoeWd67ntdt5kbVWfoetaPvY7V6mg1/reqDNHfdVKpt7nRzl2u981Ntm+hkqJp7gq41T9VqrXa5u9Iybce8lpuPcsEka3ePaXO3jUYv79dqJWz3+q+2nVWqLeuyyvqa1eZTQCSiALeBeeGFF+K+++6LN73pTfGhD33oDQGwSNatWxdr166d+X1sbCxGRkZyrKgY6rkk12i/r2brqTRdtTBS6wQ3rR0dz1v5/Eq1t+rWM/WcaLK24rZ6O6n0+q0KAnO3pWoBvNa2VE+tWVu7Kj23mdAxW6Xtq9y8VPuAVusSdtbHKim3XWXtU1wpkNe7b9WqrVXT1Tst6ck1bd1xxx3xiU98IlavXh3PPPNMLF26tGPv/Wu/9mvR398fL7/88iGPv/zyy3HMMceUfc7g4GAMDg52ojzaZPZBvFYr2eyDe7MtGd1k9kky62Wpev/Wa+ZuJ41ehuzk8mwm/GUJlFnDfp6a/QBQz/7RK5fy6R259QE899xz4+qrr46bb7457rvvvo6Gv4iI+fPnx2mnnRbf+MY3Zh6bnJyMb3zjG7Fy5cqO1kLrZe2L146Dcjcf6LNcZi13Sbcb1VN3O8JM0ZdbI5dKixTu2qWReSz6uiZNubUATkxMxPe///1YtmxZXiXE2rVr46KLLorTTz89zjzzzPjMZz4Te/bsiUsuuSS3mnpNni1htfoTZXl+O6fvlFYOoCjqPNajlfNQhOXRrnVW7bJ+EeY7L/XuT+UeSyEoU3y5BcCHHnoor7ee8ZGPfCS2b98e1157bbz00kuxYsWK+OpXv/qGgSE0Jq8DXbX3bdfXSPXSAb1VJ/cinug6FVyyboPNdtJv5TJuNtjNfW6l2uodpFWU7aiZfn1ZnluU+SQdxR1x0SFXXHFFXHHFFXmX0ZOyjkZtx/u287W7sfWj3kEdEcVo3WqFbjux1hOMWqnVr9foCN5ar9ONGu0TCu2UfAAkLVkHfZTrvF3v6M2iaDS0ZunA3mthsROqjTCvd4RnHsu90YFBWUZIF129Vxa6eV7pfQIgbVWkg9/c23RMK/dYq27t0KhmTxyV5rXe12jldHlo1a1u2hmAWzEStWj7WUSx9v1asu4v09MVeZuHrHIbBUxvKfIBcfZI1Xru5dfI+xRFs+Exz/BXpOVYS7VlVe8o40YHKTW6rtsd0Fq1bKq9fh56ZXAYaAGkad12gCvXWb1dr13Uk3MrZL1BcL3z0sp5n66x3IjwPLfbeuaxUtBrdjnlvd/mHWA73aI/Wzfs3/Q+LYC0RDccjDt10E3l4J5lnRWxj2CnAmkrbs2SZcRwHhpdJr0wD7Of28hrpHJ8oPgEQJrWLQe0Ri7J1bqPYKVWmUYu6XVKJ2sr1/rWabPnt6g38c16O5giBKhmWk+LvE9knW7utpTluDBbEdYhRLgETAuUu7wxt7N0Jw+ItS731XPpspmBIc0En3Z2pM8jkOUdAmfXUU2el4Ybed9ay7UdgxZa3Qcu6z0TI8rvD5X2lXq2uVrT1hoRn+VYMXu6eveHouw/9BYtgLRcrQEXtZ7XqveuVEfeo1trDbKo1nG+Vcu00yGnFSNu651m9nRZl12eQamZ92lkeyqK2d0EsoSoetZjPcul1qCVVgxqyRKGKwXMVrxurfkgLVoA6YisLYKz/5512lrv20nNDjBptt6iHthnn5CrteLM/j1ri0yt5+S5TOa2PJf7f6tDfRH7XdYjS2ir1EJf7oNGUZdDs1cPKq3vInYboJgEQDqmnkuo5Q7svWbufGVdPlkui/WCei/hFV0n1lvRw1+lluBGL3+34nXykLUbyuzps0xX7neoRACk46qdpGqdIIp0K49Wa2R+yg1CaUe/r3ap5yQ4LWvrWTfMf0qaaclvxXvnsT00EnjLteDVuhpS6/nV/k669AGkZbKMLq33sl6W127mPdth9gjiaiNQm7m9SLWTaTMjXtul3Kjq2Y9Xes7c/1d6fpHmNat2BJJKH5RapZltNiLbPGfZn+vdlup9j1apFfQqHSsqTTv3sdn/1nr/bt1PaB8tgDStUitUpb83+ni1acv1GazVElbpOZVqaObklTWUNTLv1R6r98RbrbWg0cCStdWiVv/PubXUCtTNdpxvhUZabCIOnb9yy6Lc/M99LI+TfbntrZGaKu23WdZx1uNKsy3u9ba0zz0uNfKelf42u+U/63NBAKQt8jr5TF8uyfJpt1V9zLrhIFvrRFUuSDfzXs0+r52tunO3j3YFwWZqrLUs6gk+rVJrG2n1++d5DKn0t6zTlpu+1uPN6objEMUiANJTKrViVRtskiV4dOqk127NhpJyLTtZO6sXSbXw30irabnWuKKrNP/1tnTX05e3aCoF6az95uqZFopGH0B6Tqc+YXfTgX727TPKPV5OrVam2a2t3Wh25/osj1d6jdn/zv1/UWTt0tCuW9IUVbl13exArCKufyhHCyA9r5GTWrkWjkp9rtpxsmtlsCp3gptdd5Zb8WR9/Va1hrRq/iv18aykmZarSsu53udmCRT1LJssfWCbeZ2sfy83fdED4rRK/TDnPlbrOVAkWgDhl2a3BmQJCu385N/uk0Y9Hdeb7W9X7zTNBtJmtOo96l1u5VpWq5meJpXWpnbOZ5YPQ1nrqXRcSGU90V0EQJJS7cRczyCEbjqoF70Fop6Roe167VZr50CadrUMd1Kzl1mbea1yz589wKWZeiodF4q+D5Iml4BpmyIPDqj1qb/cLR5a0Wcqq6JcPuqWkFtJvS07rX7vLOuv3u2qmUvN3SRrkKo0EKee12+m5a+eVu5Ga83y+r24DdBeWgBpi3InqVb1rSr390ZO9LVa/IoYfhqpKe/5yPv989SpeS/qMq6131e6/F9PN4Bq0zY7wKPVZs9frXnMOkipkW4WEKEFkDZp5e0R5vaPqjYat973qDQ4oNZtLvLQ6Kf8ZgY2tPteeb2uXZcu6xlckuctWbLcLqba71nfo1bIbEXrWD19frMs71pdUbJ+KNXyR6O0ANI2sw9krTpINXqCqPW8aiGyVv+gevsPZZXngT3PW970ygkt6zY3d/uc/n32Tz2vO1erByc1+0Gu0nyVmzbL9NXmr5XHnkqvX+19y9XeqmXYijpJmxZA2qpIB54sJ+Rqo/uqvUYnwl+7lmXWearnMnmztbbzA0OtVpRKfy/XmlappbueUdbVas3ytyyvX0/t09PPna5SrZX6qzbbWpV1H6hnn8y6vGstp9nPz9oymOeHYChHAIQEVOp83s7RqkXVyGXtSn3TKgWoIi6XcoOymgn/tf5exGWQVSu6TUDRuQRMEvRh672TdLPqadHsNXMDbaMDqXp1eU1/WKonCPbqsqB3CYAwh1F1tfXC/M8dSdmKeeqm+0OWC3zdUnu7Ndpnz/KjmwiA8EtZg1+3HuSna2+0tWf268z9t9PLpRXv1Y6Roa163XYod1m3Wj+2Zkaydus+Mm3uvtLt8wPlCIAkodnO5428TpG1qq9auX5lndDpDvXlRnSWW4ZF3T6y1Nzo6NRuCsHArxgEAr+Utb/P9HTdevf9dozQ7ZXlUU+g7cb5rVVzI4G+G5dDLb02P1COAEhDZt/6odcuj/R6J/h21tyNy6OSRvqCdsP8t2NQQzfMd6d0y7KYfeyudRskepMASEN8Qgbobi7fp00fQBqiYzRAdyt3HDcqPB0CIE1xgADoPo7duARMWdOfDCcnJ2NycjImJiYO+XHwAOgN05d9JyYmZo75k5OTrvT0OAGQQ8wOfhMTE7F///7Yu3dv7N27N15//fXYu3dvHDhwIO8yAWihefPmRV9f38zx/sCBAzOBsJtH+VOZAJi4Sn1AJiYmYu/evTE6Oho7d+6MHTt2xI4dO2Lv3r1x8ODBnKoFoB0GBgZi7969ERGxaNGiOPzww2NoaCgWLlxY9obYjd43kuIQABM33do3bXJyMg4ePBi7d++OV155JV588cV47rnnYsuWLfHzn/889u3bFxMTEzlWDECr9ff3x4IFC2L37t1x8ODBGBgYiAULFsT8+fOjr68vBgYGoq/vV8MG+vr6BMAul2wA/NSnPhX/+Z//GU899VTMnz8/du3alXdJuejr6ztkpz5w4EDs2rUrnnvuufjRj34UP/rRj+L555+P7du3x2uvvRYHDhyYuSQAQPcrlUrR19cX8+bNix07dsSuXbti9+7dsWfPnti3b1+ccMIJsXTp0hgYSDYy9KRk1+b+/fvjggsuiJUrV8add96Zdzm5GRsbi9HR0Zn+fjt37owXXnghNm3aFD/84Q/jxRdfjFdffTVef/31mfA3u8UQgO433Rgwb968ePnll2Pbtm2xdevW2Lp1a5x00kmxfPnyOPLII2NwcDD6+vri8MMPjyVLluRdNk1INgBef/31ERFx991351tIDiYmJqK/vz8iIu6999644447olQqxeLFi2PPnj3x+uuvx9jYWOzevTv27dun1Q8gEdOtgT/72c/iueeei40bN8bQ0FAsWrRo5hwxNTUVH/vYx+Liiy+OiEPPKXSPZANgI8bHx2N8fHzm97GxsRyradzk5OTMzvrkk0/Go48+mnNFABTJ+Ph4vPbaa7F9+/ayfz/zzDNnAuDscwrdw42g67B+/foYHh6e+RkZGcm7pKYtWrQo7xIA6DKLFy/OuwSa1FMB8JprrpkZml7pZ9OmTQ2//rp162J0dHTmZ8uWLS2sPh/z5s2b+f+CBQtm+oEY3QXA9CXh6VHB0wwI6X49tQY/8YlPzDRJV3LCCSc0/PqDg4MxODjY8POLaPYtXaZv+gkAEYd+OcDshgHniu7XUwFw6dKlsXTp0rzLAAAotJ4KgPWYvr3Jiy++GBMTE/HUU09FRMRb3vKWOOyww/ItDgCgjZINgNdee2187nOfm/n9t37rtyIi4n/+539i1apVOVUFANB+PTUIpB533333Id9vOP0j/AEAvS7ZAAgAkCoBEAAgMQIgAEBiBEAAgMQIgAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJGci7APJVKpVm/t/f3x8TExM5VgNAUfX398eBAwci4tBzB91JAOQQU1NTeZcAALSZAJi4sbGxmf/v27cvx0oAKLLZ54jR0dEcK6EVBMAEzW66X7ZsWSxevDgGBgbi8MMPj4MHDx4y7eTk5CG/9/W9sdvo5ORk2ccrmf2as583+3XKvWat9yn3/Hqe00mNLLNa8xERmea5ntestj6afc2s6n3NdqzTvLaTTmv1dllumka2kVqPFeU1q5m9j7ZKK7bLrMfKiIiBgYHYtWtXHDx4MJYtWzbzuMvB3UkATFB/f//M///4j/843ve+90WpVIr+/n6XgAEoq1QqzTQSjIyMzDw++5xC9xAAE1QqlWaC3rJlyw75JAcAWUyfR7QAdicBMFF2WACa4TzS3QTAxE1OTr6hnx8AVNPX15dEv9heJgAmzk4MAOlx5gcASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEpNkAHz++efj0ksvjeOPPz4WLlwYJ554Ylx33XWxf//+vEsDAGi7gbwLyMOmTZticnIybr/99njLW94SP/jBD+Kyyy6LPXv2xI033ph3eQAAbVWampqayruIIrjhhhvi1ltvjeeeey7zc8bGxmJ4eDhGR0djaGiojdUBAK3i/J3oJeByRkdH44gjjsi7DACAtkvyEvBcmzdvjptuuqnm5d/x8fEYHx+f+X1sbKzdpQEAtFxPtQBec801USqVqv5s2rTpkOds3bo1zj333Ljgggvisssuq/r669evj+Hh4ZmfkZGRds4OAEBb9FQfwO3bt8fOnTurTnPCCSfE/PnzIyJi27ZtsWrVqnjnO98Zd999d/T1Vc/D5VoAR0ZGku5DAADdRh/AHrsEvHTp0li6dGmmabdu3Rpnn312nHbaaXHXXXfVDH8REYODgzE4ONhsmQAAueqpAJjV1q1bY9WqVfHmN785brzxxti+ffvM34455pgcKwMAaL8kA+BDDz0Umzdvjs2bN8eyZcsO+VsPXREHACirpwaBZHXxxRfH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H593OUn58pe/HH/wB38Q/f39M49NTExEqVSKvr6+GB8fP+RvdB8BkIpefPHFGBsbm/l927Ztcc4558S9994bZ511VixbtizH6tKxdevWOPvss+O0006Lf/mXf3HQbbOzzjorzjzzzLjpppsi4heXIJcvXx5XXHGFQSAdMjU1FVdeeWXcf//98fDDD8dv/MZv5F1Scnbv3h0vvPDCIY9dcskl8Zu/+Ztx9dVXuxzfA/QBpKLly5cf8vthhx0WEREnnnii8NchW7dujVWrVsWb3/zmuPHGG2P79u0zfzvmmGNyrKx3rV27Ni666KI4/fTT48wzz4zPfOYzsWfPnrjkkkvyLi0Za9asiXvuuSceeOCBWLJkSbz00ksRETE8PBwLFy7Mubo0LFmy5A0hb/HixXHkkUcKfz1CAIQCe+ihh2Lz5s2xefPmN4Rujfft8ZGPfCS2b98e1157bbz00kuxYsWK+OpXv/qGgSG0z6233hoREatWrTrk8bvuuisuvvjizhcEPcglYACAxOhJDgCQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARDglyYmJuJd73pXfPjDHz7k8dHR0RgZGYm//uu/zqkygNbyVXAAs/zf//1frFixIu6444746Ec/GhERF154YXzve9+LjRs3xvz583OuEKB5AiDAHP/4j/8Yf/d3fxfPPPNMPPbYY3HBBRfExo0b49RTT827NICWEAAB5piamor3v//90d/fH08//XRceeWV8Td/8zd5lwXQMgIgQBmbNm2Kk046Kd7xjnfEE088EQMDA3mXBNAyBoEAlPHZz342Fi1aFD/5yU/ipz/9ad7lALSUFkCAOb71rW/F+973vvj6178en/zkJyMi4r//+7+jVCrlXBlAa2gBBJjl9ddfj4svvjguv/zyOPvss+POO++Mxx57LG677ba8SwNoGS2AALN8/OMfj//6r/+K733ve7Fo0aKIiLj99tvjz//8z+Ppp5+O4447Lt8CAVpAAAT4pQ0bNsQHPvCBePjhh+M973nPIX8755xz4uDBgy4FAz1BAAQASIw+gAAAiREAAQASIwACACRGAAQASIwACACQGAEQACAxAiAAQGIEQACAxAiAAACJEQABABIjAAIAJEYABABIjAAIAJAYARAAIDECIABAYgRAAIDECIAAAIkRAAEAEiMAAgAkRgAEAEiMAAgAkJj/B13FNSVMzLUVAAAAAElFTkSuQmCC", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 2\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 3\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 4\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 5\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 6\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 7\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 8\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 9\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 10\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 11\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 12\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 13\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 14\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 15\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 16\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 17\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 18\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 19\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 20\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 21\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 22\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 23\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 24\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 25\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 26\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 27\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 28\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 29\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 30\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 31\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 32\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 33\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 34\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 35\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 36\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 37\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 38\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 39\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 40\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 41\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 42\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 43\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 44\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 45\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 46\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 47\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 48\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 49\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 50\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 51\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 52\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 53\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 54\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 55\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 56\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 57\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 58\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 59\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 60\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 61\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 62\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 63\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 64\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 65\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 66\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 67\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 68\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 69\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 70\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 71\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 72\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 73\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 74\n", - "Starting forward run...\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.9/site-packages/meep/geom.py:582: UserWarning: The weights parameter of MaterialGrid must be in the range [0,1].\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 75\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 76\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 77\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 78\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 79\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 80\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 81\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 82\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 83\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 84\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 85\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 86\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 87\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 88\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 89\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 90\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 91\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 92\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 93\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 94\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 95\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 96\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 97\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 98\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 99\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 100\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 101\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 102\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 103\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 104\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 105\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 106\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 107\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 108\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 109\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 110\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 111\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 112\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 113\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 114\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 115\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 116\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 117\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 118\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 119\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 120\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 121\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 122\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 123\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 124\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 125\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 126\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 127\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 128\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 129\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 130\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 131\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 132\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 133\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 134\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 135\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 136\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 137\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 138\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 139\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 140\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 141\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 142\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 143\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 144\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 145\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 146\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 147\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 148\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 149\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n", - "Current iteration: 150\n", - "Starting forward run...\n", - "Starting adjoint run...\n", - "Calculating gradient...\n" - ] - } - ], - "source": [ - "evaluation_history = []\n", - "connectivity_s_history = []\n", - "connectivity_v_history = []\n", - "geom_s_history = []\n", - "geom_v_history = []\n", - "cur_iter = [0]\n", - "\n", - "def f(v, gradient, beta):\n", - " print(\"Current iteration: {}\".format(cur_iter[0] + 1))\n", - "\n", - " f0, dJ_du = opt([mapping(v, eta_i, beta)]) # compute objective and gradient\n", - "\n", - " if gradient.size > 0:\n", - " gradient[:] = tensor_jacobian_product(mapping, 0)(\n", - " v, eta_i, beta, dJ_du\n", - " ) # backprop\n", - "\n", - " evaluation_history.append(np.real(f0))\n", - " cur_iter[0] = cur_iter[0] + 1\n", - "\n", - " return np.real(f0)\n", - "\n", - "def connect_s(x,gradient,eta,beta): \n", - "\n", - " v0 = x[:] # design parameters \n", - " v = mapping_s(v0,eta_i,beta)#pre-process with mapping_s\n", - " _, f0, dJ_du = mpa.constraint_connectivity(v, Nx, 1, Ny, p=3, cond_s=500, thresh=30)\n", - " dJ_du = np.squeeze(dJ_du) \n", - " my_grad = tensor_jacobian_product(mapping_s,0)(v0,eta,beta,dJ_du)\n", - " gradient[:] = my_grad.flatten()\n", - "\n", - " connectivity_s_history.append(f0)\n", - " return f0\n", - "\n", - "def connect_v(x,gradient,eta,beta): \n", - "\n", - " v0 = x[:] # design parameters \n", - " v = mapping_v(v0,eta_i,beta)#pre-process with mapping_v\n", - " _, f0, dJ_du = mpa.constraint_connectivity(v, Nx, 1, Ny, p=3, cond_s=500, thresh=30)\n", - " dJ_du = np.squeeze(dJ_du) \n", - " my_grad = tensor_jacobian_product(mapping_v,0)(v0,eta,beta,dJ_du)\n", - " gradient[:] = my_grad.flatten()\n", - "\n", - " connectivity_v_history.append(f0)\n", - " return f0\n", - "\n", - "def geom_s(x,gradient,eta,beta):\n", - " \n", - " threshf = lambda v: mpa.tanh_projection(v,beta,eta)\n", - " filterf = lambda v: mpa.conic_filter(v.reshape(Nx, Ny),filter_radius,design_region_width,design_region_height,design_region_resolution)\n", - " v = x[:]\n", - "\n", - " v = npa.where(bottom_mask.flatten(),1,npa.where(Air_mask.flatten(),0,v))\n", - "\n", - " v_c=(filter_radius/resolution)**4 \n", - " g_s = mpa.constraint_solid(v,v_c,eta_e,filterf,threshf,1) \n", - " g_s_grad = grad(mpa.constraint_solid,0)(v,v_c,eta_e,filterf,threshf,1) \n", - " gradient[:] = npa.where(bottom_mask.flatten() | Air_mask.flatten(),0,g_s_grad)\n", - " geom_s_history.append(g_s)\n", - " return float(g_s)-1e-3\n", - "\n", - "def geom_v(x,gradient,eta,beta):\n", - " \n", - " threshf = lambda v: mpa.tanh_projection(v,beta,eta)\n", - " filterf = lambda v: mpa.conic_filter(v.reshape(Nx, Ny),filter_radius,design_region_width,design_region_height,design_region_resolution)\n", - " v = x[:]\n", - "\n", - " v = npa.where(bottom_mask.flatten(),1,npa.where(Air_mask.flatten(),0,v))\n", - " v_c=(filter_radius/resolution)**4 \n", - " g_v = mpa.constraint_void(v,v_c,eta_d,filterf,threshf,1) \n", - " g_v_grad = grad(mpa.constraint_void,0)(v,v_c,eta_d,filterf,threshf,1) \n", - " gradient[:] = npa.where(bottom_mask.flatten() | Air_mask.flatten(),0,g_v_grad)\n", - " geom_v_history.append(g_v)\n", - " return float(g_v)-1e-3\n", - "\n", - "\n", - "%matplotlib ipympl\n", - "# Create the animation\n", - "animate = Animate2D(\n", - " fields=None,\n", - " realtime=True,\n", - " eps_parameters={'contour': False, 'alpha': 1, 'frequency': 1/1.55},\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - " update_epsilon=True, # required for the geometry to update dynamically\n", - " nb=True # required if running in a Jupyter notebook\n", - ")\n", - "# This will trigger the animation at the end of each simulation\n", - "opt.step_funcs=[mp.at_end(animate)]\n", - "\n", - "\n", - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny # number of parameters\n", - "\n", - "# Initial guess\n", - "x = np.ones((n,)) * 0.5\n", - "\n", - "# lower and upper bounds\n", - "lb = np.zeros((Nx * Ny,))\n", - "ub = np.ones((Nx * Ny,))\n", - "\n", - "\n", - "# Length-scale constraints are introduced in the last beta, as they requires almost binaried structures.\n", - "# Connectivity constraints are less well-defined when there are too many grey scale pixels, but they can \n", - "# be introduced earlier to guide the evolution in the right direction before the structure gets too disconnected.\n", - "\n", - "cur_beta = 8\n", - "beta_scale = 2\n", - "num_betas = 3\n", - "update_factor = 50\n", - "ftol = 1e-5\n", - "for iters in range(num_betas):\n", - " design_variables.beta = cur_beta\n", - " solver = nlopt.opt(algorithm, n)\n", - " solver.set_lower_bounds(lb)\n", - " solver.set_upper_bounds(ub)\n", - " solver.set_max_objective(lambda a, g: f(a, g, cur_beta))\n", - " if cur_beta >= 16:\n", - " solver.add_inequality_constraint(lambda x,g: connect_s(x,g,eta_i, cur_beta), 1e-3)\n", - " solver.add_inequality_constraint(lambda x,g: connect_v(x,g,eta_i, cur_beta), 1e-3)\n", - " solver.add_inequality_constraint(lambda x,g: geom_s(x,g,eta_i, cur_beta), 1e-3)\n", - " solver.add_inequality_constraint(lambda x,g: geom_v(x,g,eta_i, cur_beta), 1e-3)\n", - " solver.set_maxeval(update_factor)\n", - " solver.set_ftol_rel(ftol)\n", - " x[:] = solver.optimize(x)\n", - " cur_beta = cur_beta * beta_scale" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "a5da86a6", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - " \n", - "
\n", - " \n", - "
\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
\n", - "
\n", - "\n", - "\n", - "\n" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "animate.to_jshtml(fps=5)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "1b4a1358", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD4CAYAAAD8Zh1EAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAAsTAAALEwEAmpwYAAAmpUlEQVR4nO3deXQc5Z3u8e+vepXUkncbb9hmCWBjywbFAROMAw57WGbIBeLDkkzCyU0CzDB3AhnuSW5yM3cy9+RkIeGQwyRA4BJwQgiBAMkkbIawBBsIYAzEgMEyXmR5kyz1/t4/qiTLRkKy3HJbVc/nnD7q7qqu/lVX6+m336p+y5xziIjI8OdVuwAREakMBbqISEgo0EVEQkKBLiISEgp0EZGQiFfriceOHeumT59eracXERmWVqxYsdk5N663aVUL9OnTp7N8+fJqPb2IyLBkZu/2NU1dLiIiIaFAFxEJCQW6iEhI9NuHbmZTgduBCYADbnbO/XCPeRYBvwXeCe661zn3rYpWKiJDplAo0NzcTDabrXYpEkin00yZMoVEIjHgxwxkp2gR+Gfn3AtmVg+sMLM/Oude22O+J51zZ+9FvSJygGhubqa+vp7p06djZtUuJ/Kcc7S2ttLc3MyMGTMG/Lh+u1ycc+udcy8E19uAVcDkQVcqIgecbDbLmDFjFOYHCDNjzJgxe/2Naa/60M1sOjAPeK6Xyceb2V/N7GEzm9XH468ws+VmtrylpWWvChWRoaUwP7AMZnsMONDNLAP8GvhH59yOPSa/AExzzjUCPwLu620ZzrmbnXNNzrmmceN6PS6+X5t2ZPm3B19j/fbOQT1eRCSsBhToZpbAD/M7nXP37jndObfDOdceXH8ISJjZ2IpWGnj2nS3c8uc1LPy/j/Evv/orqze1DcXTiEiErVmzhl/84hcVWdb06dPZvHkzAAsWLOh1nssvv5x77rlnn5+r30A3v93/M2CVc+57fcxzUDAfZjY/WG7rPlfXi3MaJ/H4/1jEZ+YfzAMvv8/pP3iSNzYo1EWkcioZ6D09/fTTFV9mTwNpoZ8AXAKcbGYvBZczzeyLZvbFYJ4LgFfN7K/ADcBFbghPhTR1dC3fPPdo/vhPJ1EsOx57Y9NQPZWI7Ce33347c+bMobGxkUsuuQTwg/Xkk09mzpw5nHLKKbz33nuA36K96qqrWLBgAYccckh36/bxxx9n0aJFXHDBBRx55JEsWbKErihasWIFJ510EsceeyynnXYa69evB2D16tUsXryYxsZGjjnmGN566y2uu+46nnzySebOncv3v//93epcv349CxcuZO7cuRx99NE8+eSTANx1113Mnj2bo48+mmuvvbbXdcxkMoB/FMtXvvIVjjjiCBYvXsymTZXJsH4PW3TOPQV8aO+8c+7HwI8rUtFemDq6lkPG1fH8O1v44kmH7u+nFwmlbz6wktfe33M32b6ZOamBb3yq12MlAFi5ciXf/va3efrppxk7dixbtmwB4Morr+Syyy7jsssu45ZbbuGqq67ivvvuA/xgfeqpp3j99dc555xzuOCCCwB48cUXWblyJZMmTeKEE07gz3/+Mx/72Me48sor+e1vf8u4ceNYunQp119/PbfccgtLlizhuuuu4/zzzyebzVIul/nOd77Dd7/7XX73u999oNZf/OIXnHbaaVx//fWUSiU6Ojp4//33ufbaa1mxYgWjRo3i1FNP5b777uO8887rdX1/85vf8MYbb/Daa6+xceNGZs6cyec+97l9e5Gp4uBclTJ/+mgefnUD5bLD87SXXmQ4evTRR/n0pz/N2LH+rrfRo0cD8Mwzz3Dvvf5uu0suuYSvfvWr3Y8577zz8DyPmTNnsnHjxu7758+fz5QpUwCYO3cua9asYeTIkbz66qt88pOfBKBUKjFx4kTa2tpYt24d559/PuD/mKc/H/3oR/nc5z5HoVDgvPPOY+7cuTz66KMsWrSIroM9lixZwrJly/oM9GXLlnHxxRcTi8WYNGkSJ5988t68XH0a9oH+0emjufv5tby5qY0jD2qodjkiw96HtaQPJKlUqvt6zx7envfHYjGKxSLOOWbNmsUzzzyz2zLa2vZ+/9vChQtZtmwZDz74IJdffjnXXHMNI0aMGMQaVN6wH8tl/gz/k/z5d7ZUuRIRGayTTz6ZX/3qV7S2+sdSdHW5LFiwgLvvvhuAO++8kxNPPHFQyz/iiCNoaWnpDvRCocDKlSupr69nypQp3d04uVyOjo4O6uvr+wz7d999lwkTJvCFL3yBz3/+87zwwgvMnz+fJ554gs2bN1Mqlbjrrrs46aST+qxn4cKFLF26lFKpxPr163nssccGtV57GvaBPmVUDQc1pHlOgS4ybM2aNYvrr7+ek046icbGRq655hoAfvSj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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "%matplotlib inline\n", - "plt.plot(connectivity_s_history, label=\"connect solid\")\n", - "plt.plot(connectivity_v_history, label=\"connect void\")\n", - "plt.legend()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "807663b9", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "%matplotlib inline\n", - "plt.plot(evaluation_history)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "2fa052c5", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5e11667180654900b55b457cc5730538", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatProgress(value=0.0, description='0% done ', max=500.0)" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "cwsrc = [mp.Source(mp.ContinuousSource(frequency=fcen), component=mp.Ez, size=source_size, center=source_center)]\n", - "\n", - "sim = mp.Simulation(\n", - " cell_size=mp.Vector3(Sx, 15),\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=cwsrc,\n", - " default_material=Air,\n", - " resolution=resolution,\n", - ")\n", - "\n", - "sim.run(until=500)\n", - "%matplotlib inline\n", - "sim.plot2D(fields=mp.Ez)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "12a9a63d", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "opt.plot2D(True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "33034f2a", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/python/examples/adjoint_optimization/.ipynb_checkpoints/Bend Minimax-checkpoint.ipynb b/python/examples/adjoint_optimization/.ipynb_checkpoints/Bend Minimax-checkpoint.ipynb deleted file mode 100644 index a15e36dea..000000000 --- a/python/examples/adjoint_optimization/.ipynb_checkpoints/Bend Minimax-checkpoint.ipynb +++ /dev/null @@ -1,504 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Minimax Optimization using the Epigraph Formulation\n", - "\n", - "Our previous tutorials have demonstrated a mean squared error approach to minimization. In some applications, it's beneficial to perform a minimax optimization, where we minimize the worst performing error over a set of objective functions.\n", - "\n", - "Because meep's adjoint solver can calculate the gradient at multiple frequencies points using the same forward and adjoint solves, it's relatively easy to perform a minimax optimization for our bent waveguide example.\n", - "\n", - "Most minimax problems are difficult to solve because the gradient breaks down with min/max functions. We can overcome this by using the popular epigraph formulation, which introduces a dummy parameter and transforms the various additional objective functions as constraints.\n", - "\n", - "In this case, we'll generate a vector-valued objective function -- each element of said vector corresponds to the performance at each frequency point." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import meep as mp\n", - "import meep.adjoint as mpa\n", - "import numpy as np\n", - "from autograd import numpy as npa\n", - "from autograd import tensor_jacobian_product, grad\n", - "import nlopt\n", - "from matplotlib import pyplot as plt\n", - "from matplotlib.patches import Circle\n", - "from scipy import special, signal\n", - "\n", - "mp.quiet(quietval=True)\n", - "Si = mp.Medium(index=3.4)\n", - "SiO2 = mp.Medium(index=1.44)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use the same setup as our original bent waveguide example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "waveguide_width = 0.5\n", - "design_region_width = 2.5\n", - "design_region_height = 2.5\n", - "\n", - "waveguide_length = 0.5\n", - "\n", - "pml_size = 1.0\n", - "\n", - "resolution = 30\n", - "\n", - "nf = 10\n", - "frequencies = 1 / np.linspace(1.5, 1.6, nf)\n", - "\n", - "minimum_length = 0.09 # minimum length scale (microns)\n", - "eta_i = (\n", - " 0.5 # blueprint (or intermediate) design field thresholding point (between 0 and 1)\n", - ")\n", - "eta_e = 0.55 # erosion design field thresholding point (between 0 and 1)\n", - "eta_d = 1 - eta_e # dilation design field thresholding point (between 0 and 1)\n", - "filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)\n", - "print(filter_radius)\n", - "design_region_resolution = int(1 * resolution)\n", - "\n", - "Sx = 2 * pml_size + 2 * waveguide_length + design_region_width\n", - "Sy = 2 * pml_size + design_region_height + 0.5\n", - "cell_size = mp.Vector3(Sx, Sy)\n", - "\n", - "pml_layers = [mp.PML(pml_size)]\n", - "\n", - "fcen = 1 / 1.55\n", - "width = 0.2\n", - "fwidth = width * fcen\n", - "source_center = [-Sx / 2 + pml_size + waveguide_length / 3, 0, 0]\n", - "source_size = mp.Vector3(0, Sy, 0)\n", - "kpoint = mp.Vector3(1, 0, 0)\n", - "src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)\n", - "source = [\n", - " mp.EigenModeSource(\n", - " src,\n", - " eig_band=1,\n", - " direction=mp.NO_DIRECTION,\n", - " eig_kpoint=kpoint,\n", - " size=source_size,\n", - " center=source_center,\n", - " )\n", - "]\n", - "\n", - "Nx = int(design_region_resolution * design_region_width)\n", - "Ny = int(design_region_resolution * design_region_height)\n", - "\n", - "design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type=\"U_MEAN\")\n", - "design_region = mpa.DesignRegion(\n", - " design_variables,\n", - " volume=mp.Volume(\n", - " center=mp.Vector3(),\n", - " size=mp.Vector3(design_region_width, design_region_height, 0),\n", - " ),\n", - ")\n", - "\n", - "x_g = np.linspace(-design_region_width / 2, design_region_width / 2, Nx)\n", - "y_g = np.linspace(-design_region_height / 2, design_region_height / 2, Ny)\n", - "X_g, Y_g = np.meshgrid(x_g, y_g, sparse=True, indexing=\"ij\")\n", - "\n", - "left_wg_mask = (X_g == -design_region_width / 2) & (np.abs(Y_g) <= waveguide_width / 2)\n", - "top_wg_mask = (Y_g == design_region_width / 2) & (np.abs(X_g) <= waveguide_width / 2)\n", - "Si_mask = left_wg_mask | top_wg_mask\n", - "\n", - "border_mask = (\n", - " (X_g == -design_region_width / 2)\n", - " | (X_g == design_region_width / 2)\n", - " | (Y_g == -design_region_height / 2)\n", - " | (Y_g == design_region_height / 2)\n", - ")\n", - "SiO2_mask = border_mask.copy()\n", - "SiO2_mask[Si_mask] = False\n", - "\n", - "\n", - "def mapping(x, eta, beta):\n", - " x = npa.where(Si_mask.flatten(), 1, npa.where(SiO2_mask.flatten(), 0, x))\n", - " # filter\n", - " filtered_field = mpa.conic_filter(\n", - " x,\n", - " filter_radius,\n", - " design_region_width,\n", - " design_region_height,\n", - " design_region_resolution,\n", - " )\n", - "\n", - " # projection\n", - " projected_field = mpa.tanh_projection(filtered_field, beta, eta)\n", - "\n", - " # interpolate to actual materials\n", - " return projected_field.flatten()\n", - "\n", - "\n", - "geometry = [\n", - " mp.Block(\n", - " center=mp.Vector3(x=-Sx / 4), material=Si, size=mp.Vector3(Sx / 2, 0.5, 0)\n", - " ), # horizontal waveguide\n", - " mp.Block(\n", - " center=mp.Vector3(y=Sy / 4), material=Si, size=mp.Vector3(0.5, Sy / 2, 0)\n", - " ), # vertical waveguide\n", - " mp.Block(\n", - " center=design_region.center, size=design_region.size, material=design_variables\n", - " ), # design region\n", - " mp.Block(\n", - " center=design_region.center,\n", - " size=design_region.size,\n", - " material=design_variables,\n", - " e1=mp.Vector3(x=-1).rotate(mp.Vector3(z=1), np.pi / 2),\n", - " e2=mp.Vector3(y=1).rotate(mp.Vector3(z=1), np.pi / 2),\n", - " ),\n", - "]\n", - "\n", - "sim = mp.Simulation(\n", - " cell_size=cell_size,\n", - " boundary_layers=pml_layers,\n", - " geometry=geometry,\n", - " sources=source,\n", - " default_material=SiO2,\n", - " resolution=resolution,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we'll slightly modify our objective function. We'll remove the `mean` function which was previously required to ensure our objective function is scalar valued. Instead, our objective function will be vector valued. We'll also return the *error* of our bend (`1-power`) rather than the raw power itself. This way we can formulate a minimax problem rather than a maximin problem -- although we could setup our optimization problem that way too." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mode = 1\n", - "\n", - "TE0 = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(x=-Sx / 2 + pml_size + 2 * waveguide_length / 3),\n", - " size=mp.Vector3(y=Sy),\n", - " ),\n", - " mode,\n", - ")\n", - "TE_top = mpa.EigenmodeCoefficient(\n", - " sim,\n", - " mp.Volume(\n", - " center=mp.Vector3(0, Sx / 2 - pml_size - 2 * waveguide_length / 3, 0),\n", - " size=mp.Vector3(x=Sx),\n", - " ),\n", - " mode,\n", - ")\n", - "ob_list = [TE0, TE_top]\n", - "\n", - "\n", - "def J(source, top):\n", - " power = npa.abs(top / source)\n", - " return npa.abs(1 - power)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt = mpa.OptimizationProblem(\n", - " simulation=sim,\n", - " objective_functions=J,\n", - " objective_arguments=ob_list,\n", - " design_regions=[design_region],\n", - " frequencies=frequencies,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Our objective function that we pass to nlopt is rather simple. We'll introduce a dummy parameter `t`. The goal of the optimization problem will be to simply minimize the value of `t`. The gradient of this functional is rather straightforward." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "evaluation_history = []\n", - "cur_iter = [0]\n", - "\n", - "\n", - "def f(x, grad):\n", - " t = x[0] # \"dummy\" parameter\n", - " v = x[1:] # design parameters\n", - " if grad.size > 0:\n", - " grad[0] = 1\n", - " grad[1:] = 0\n", - " return t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The key to the epigraph formulation (and most nonlinear optimization problems) lies in the constraints. We'll define one constraint for every frequency point of interest ($f_i$). We'll define our constraint as \n", - "\n", - "$$f_i < t$$\n", - "\n", - "where $t$ is the previosuly defined dummy parameter. Each constraint function is then defined by\n", - "\n", - "$$ c_i = f_i-t $$\n", - "\n", - "within some tolerance.\n", - "\n", - "More detail about this formulation can be found in the nlopt [documentation](https://nlopt.readthedocs.io/en/latest/NLopt_Introduction/#equivalent-formulations-of-optimization-problems)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def c(result, x, gradient, eta, beta):\n", - " print(\n", - " \"Current iteration: {}; current eta: {}, current beta: {}\".format(\n", - " cur_iter[0], eta, beta\n", - " )\n", - " )\n", - "\n", - " t = x[0] # dummy parameter\n", - " v = x[1:] # design parameters\n", - "\n", - " f0, dJ_du = opt([mapping(v, eta, beta)])\n", - "\n", - " # Backprop the gradients through our mapping function\n", - " my_grad = np.zeros(dJ_du.shape)\n", - " for k in range(opt.nf):\n", - " my_grad[:, k] = tensor_jacobian_product(mapping, 0)(v, eta, beta, dJ_du[:, k])\n", - "\n", - " # Assign gradients\n", - " if gradient.size > 0:\n", - " gradient[:, 0] = -1 # gradient w.r.t. \"t\"\n", - " gradient[:, 1:] = my_grad.T # gradient w.r.t. each frequency objective\n", - "\n", - " result[:] = np.real(f0) - t\n", - "\n", - " # store results\n", - " evaluation_history.append(np.real(f0))\n", - "\n", - " # visualize\n", - " plt.figure()\n", - " ax = plt.gca()\n", - " opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - " )\n", - " circ = Circle((2, 2), minimum_length / 2)\n", - " ax.add_patch(circ)\n", - " ax.axis(\"off\")\n", - " plt.show()\n", - "\n", - " cur_iter[0] = cur_iter[0] + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now run our optimizer in loop. The loop will increase beta and reset the optimizer, which is important since the cost function changes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "algorithm = nlopt.LD_MMA\n", - "n = Nx * Ny # number of parameters\n", - "\n", - "# Initial guess\n", - "x = np.ones((n,)) * 0.5\n", - "x[Si_mask.flatten()] = 1 # set the edges of waveguides to silicon\n", - "x[SiO2_mask.flatten()] = 0 # set the other edges to SiO2\n", - "\n", - "# lower and upper bounds\n", - "lb = np.zeros((Nx * Ny,))\n", - "lb[Si_mask.flatten()] = 1\n", - "ub = np.ones((Nx * Ny,))\n", - "ub[SiO2_mask.flatten()] = 0\n", - "\n", - "# insert dummy parameter bounds and variable\n", - "x = np.insert(x, 0, 0.5) # our initial guess for the worst error\n", - "lb = np.insert(lb, 0, 0) # we can't get less than 0 error!\n", - "ub = np.insert(ub, 0, 1) # we can't get more than 1 error!\n", - "\n", - "cur_beta = 4\n", - "beta_scale = 2\n", - "num_betas = 6\n", - "update_factor = 12\n", - "for iters in range(num_betas):\n", - " solver = nlopt.opt(algorithm, n + 1)\n", - " solver.set_lower_bounds(lb)\n", - " solver.set_upper_bounds(ub)\n", - " solver.set_min_objective(f)\n", - " solver.set_maxeval(update_factor)\n", - " solver.add_inequality_mconstraint(\n", - " lambda r, x, g: c(r, x, g, eta_i, cur_beta), np.array([1e-3] * nf)\n", - " )\n", - " x[:] = solver.optimize(x)\n", - " cur_beta = cur_beta * beta_scale" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since we are optimizing over the entire frequency band, we can visualize the upper and lower bounds of the device's performance as a function of iteration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "lb = 1 - np.min(evaluation_history, axis=1)\n", - "ub = 1 - np.max(evaluation_history, axis=1)\n", - "mean = 1 - np.mean(evaluation_history, axis=1)\n", - "\n", - "num_iters = lb.size\n", - "\n", - "plt.figure()\n", - "plt.fill_between(\n", - " np.arange(1, num_iters + 1), 10 * np.log10(lb), 10 * np.log10(ub), alpha=0.3\n", - ")\n", - "plt.plot(10 * np.log10(mean), \"o-\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Iteration\")\n", - "plt.ylabel(\"Average Insertion Loss (dB)\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot our results and see the resulting geometry." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "opt.update_design([mapping(x[1:], eta_i, cur_beta)])\n", - "plt.figure()\n", - "ax = plt.gca()\n", - "opt.plot2D(\n", - " False,\n", - " ax=ax,\n", - " plot_sources_flag=False,\n", - " plot_monitors_flag=False,\n", - " plot_boundaries_flag=False,\n", - ")\n", - "circ = Circle((2, 2), minimum_length / 2)\n", - "ax.add_patch(circ)\n", - "ax.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As before, we'll check the final frequency response. We see that the response is much \"flatter\" across the band, and the worst performing frequency is much better than with the MSE approach." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f0, dJ_du = opt([mapping(x[1:], eta_i, cur_beta)], need_gradient=False)\n", - "frequencies = opt.frequencies\n", - "source_coef, top_coef = opt.get_objective_arguments()\n", - "\n", - "top_profile = np.abs(top_coef / source_coef) ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(1 / frequencies, top_profile * 100, \"-o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Wavelength (microns)\")\n", - "plt.ylabel(\"Transmission (%)\")\n", - "plt.ylim(98, 100)\n", - "plt.show()\n", - "\n", - "plt.figure()\n", - "plt.plot(1 / frequencies, 10 * np.log10(top_profile), \"-o\")\n", - "plt.grid(True)\n", - "plt.xlabel(\"Wavelength (microns)\")\n", - "plt.ylabel(\"Insertion Loss (dB)\")\n", - "plt.ylim(-0.1, 0)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In summary, it is very easy to implement design constraints and density parameter operations using the native adjoint solver interface. One could use this same design flow to implement robus optimization over many frequency points." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -}