Files
modeconverter/sim_opt_design.py
T

220 lines
6.5 KiB
Python

#this file serves to simulate the optimized design parameters
#it imports data from the optimization process and runs a meep simulation to verify the results
import faulthandler
faulthandler.enable()
from typing import List, NamedTuple, Tuple
from autograd import numpy as npa, tensor_jacobian_product, grad
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import meep as mp
import meep.adjoint as mpa
import nlopt
import numpy as np
import os
import xarray as xr
from PIL import Image
import pickle
if not os.path.exists("sim_opt_design_results"):
os.makedirs("sim_opt_design_results")
#import data from previous savez file
try:
with open("results/optimal_design.pkl", "rb") as f:
data = pickle.load(f)
RESOLUTION=data['RESOLUTION']
MAX_RUN_TIME = data['MAX_RUN_TIME']
WAVELENGTH_MIN_UM = data['WAVELENGTH_MIN_UM']
WAVELENGTH_MAX_UM = data['WAVELENGTH_MAX_UM']
DESIGN_REGION_CENTER = data['DESIGN_REGION_CENTER']
SILICON = data['SILICON']
SILICON_DIOXIDE = data['SILICON_DIOXIDE']
AIR = data['AIR']
cell_um = data['cell_um']
SUBSTRATE_THICKNESS = data['SUBSTRATE_THICKNESS']
SUBSTRATE_SIZE = data['SUBSTRATE_SIZE']
SUBSTRATE_CENTER = data['SUBSTRATE_CENTER']
physical_domains_size = data['physical_domains_size']
frequency_min = data['frequency_min']
frequency_max = data['frequency_max']
frequency_center = data['frequency_center']
frequency_width = data['frequency_width']
frequencies = data['frequencies']
num_wavelength = data['num_wavelengths']
SOURCE_CENTER = data['SOURCE_CENTER']
SOURCE_SIZE = data['SOURCE_SIZE']
NEAR_REGION_MONITOR_CENTER = data['NEAR_REGION_MONITOR_CENTER']
NEAR_REGION_MONITOR_SIZE = data['NEAR_REGION_MONITOR_SIZE']
FF_MONITOR_CENTER = data['FF_MONITOR_CENTER']
FF_MONITOR_SIZE = data['FF_MONITOR_SIZE']
xs = data['xs']
ys = data['ys']
ff_points = data['ff_points']
DESIGN_WAVELENGTHS_UM=data['DESIGN_WAVELENGTHS_UM']
BUFFER=data['BUFFER']
PML_UM=data['PML_UM']
DESIGN_REGION_UM=data['DESIGN_REGION_UM']
DESIGN_REGION_RESOLUTION=data['DESIGN_REGION_RESOLUTION']
NX_DESIGN_GRID=data['NX_DESIGN_GRID']
NY_DESIGN_GRID=data['NY_DESIGN_GRID']
MIN_LENGTH_UM=data['MIN_LENGTH_UM']
sigmoid_biases=data['sigmoid_biases']
max_eval=data['max_eval']
objfunc_history=data['objfunc_history']
epivar_history=data['epivar_history']
epigraph_variable=data['epigraph_variable']
unmapped_design_weights=data['unmapped_design_weights']
optimal_design_weights=data['optimal_design_weights']
except FileNotFoundError:
raise FileNotFoundError("Optimal design data not found.")
print(optimal_design_weights)
pml_layers = [mp.PML(PML_UM)]
view_2D_plane_xz = mp.Volume(
center=mp.Vector3(0,0,0),
size=mp.Vector3(cell_um.x,0,cell_um.z)
)
view_2D_plane_xy = mp.Volume(
center=mp.Vector3(0,0,DESIGN_REGION_CENTER.z/2),
size=mp.Vector3(cell_um.x,cell_um.y,0)
)
matgrid = mp.MaterialGrid(
grid_size = mp.Vector3(NX_DESIGN_GRID, NY_DESIGN_GRID,0),
medium1 = AIR,
medium2 = SILICON,
weights = optimal_design_weights.reshape((NX_DESIGN_GRID, NY_DESIGN_GRID))
)
matgrid_region = mpa.DesignRegion(
matgrid,
volume=mp.Volume(
center=DESIGN_REGION_CENTER,
size=DESIGN_REGION_UM
)
)
matgrid_block = mp.Block(
center=matgrid_region.center,
size=matgrid_region.size,
material=matgrid,
)
substrate_block = mp.Block(
center = SUBSTRATE_CENTER,
size = SUBSTRATE_SIZE,
material = SILICON_DIOXIDE,
)
geometry = [
matgrid_block,
substrate_block,
]
sources = [
mp.Source(
src=mp.GaussianSource(frequency_center, fwidth=frequency_width),
component=mp.Ex,
size=SOURCE_SIZE,
center=SOURCE_CENTER,
)
]
sim = mp.Simulation(
resolution=RESOLUTION,
default_material=AIR,
cell_size=cell_um,
sources=sources,
geometry=geometry,
boundary_layers=pml_layers,
k_point=mp.Vector3(),
)
plt.figure()
ax = plt.gca()
sim.plot2D(output_plane=view_2D_plane_xz, ax=ax)
plt.savefig(f"sim_opt_design_results/simulation_setup.pdf")
plt.close()
NearRegions = [
mp.Near2FarRegion(
center=NEAR_REGION_MONITOR_CENTER,
size=NEAR_REGION_MONITOR_SIZE,
weight=+1,
)
]
FarFields = mpa.Near2FarFields(
sim=sim,
Near2FarRegions=NearRegions,
far_pts=ff_points,
)
obj_list = [FarFields]
def dummy_J1(ff):
return [1]*num_wavelength
opt = mpa.OptimizationProblem(
simulation=sim,
objective_functions=[dummy_J1],
objective_arguments=[FarFields],
design_regions=[matgrid_region],
frequencies=frequencies,
maximum_run_time=MAX_RUN_TIME,
)
#make a plot of the optimization setup
plt.figure(figsize=(12,6))
ax1 = plt.subplot(1,2,1)
ax2 = plt.subplot(1,2,2)
opt.plot2D(True, output_plane=view_2D_plane_xz, ax=ax1)
opt.plot2D(True, output_plane=view_2D_plane_xy, ax=ax2)
plt.savefig(f"sim_opt_design_results/optimization_setup.pdf")
plt.close()
print(type(optimal_design_weights), optimal_design_weights.shape)
dummy = opt(
[
optimal_design_weights.flatten()
],
need_gradient=False,
)
#note that get_objective_arguments returns
# a LIST of the arguments passed to the obj. fun.
#we select the first list element, since we ony have one obj. fun.
solved_ff = opt.get_objective_arguments()[0]
def intensity_from_farfields(FarFields) -> np.ndarray:
'''Computes the intensity pattern at the monitor location from the simulated far-fields
Args:
FarFields: the simulated far-field patterns at the farfield
monitor location, for each wavelength
Returns:
A 2D-array [Nx*Ny,num_wavelengths] of intensities, for each wavelength,
at the monitor location.
'''
intensity_x = npa.abs(FarFields[:,:,0]) ** 2
return intensity_x
solved_intensity = intensity_from_farfields(solved_ff)
#make a plot of the ff intensities (at the center frequency)
array_to_plot = solved_intensity[:,1].reshape(NX_DESIGN_GRID, NY_DESIGN_GRID)
plt.figure()
plt.imshow(array_to_plot, origin='lower', extent=(-cell_um.x/2,cell_um.x/2,-cell_um.y/2,cell_um.y/2))
plt.xlabel("x (um)")
plt.ylabel("y (um)")
plt.title("Simulated far-field intensity at center frequency")
plt.colorbar(label="Intensity (a.u.)")
plt.savefig(f"sim_opt_design_results/simulated_farfield_intensity.pdf")
plt.close()