2016-03-30 15:00:00 -07:00
|
|
|
"""
|
|
|
|
Example code for running an OpenCL FDTD simulation
|
|
|
|
|
|
|
|
See main() for simulation setup.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import time
|
2017-08-12 19:20:29 -07:00
|
|
|
import logging
|
2019-08-30 22:13:26 -07:00
|
|
|
import pyopencl
|
2016-03-30 15:00:00 -07:00
|
|
|
|
|
|
|
import numpy
|
2017-08-14 15:41:20 -07:00
|
|
|
import lzma
|
2016-09-01 14:39:44 -07:00
|
|
|
import dill
|
2016-03-30 15:00:00 -07:00
|
|
|
|
2017-08-14 15:41:20 -07:00
|
|
|
from opencl_fdtd import Simulation
|
2016-03-30 15:00:00 -07:00
|
|
|
from masque import Pattern, shapes
|
|
|
|
import gridlock
|
|
|
|
import pcgen
|
2020-10-16 19:32:22 -07:00
|
|
|
import meanas
|
2016-09-01 14:39:44 -07:00
|
|
|
|
|
|
|
|
|
|
|
__author__ = 'Jan Petykiewicz'
|
2016-03-30 15:00:00 -07:00
|
|
|
|
2017-08-12 19:20:29 -07:00
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2016-03-30 15:00:00 -07:00
|
|
|
|
|
|
|
def perturbed_l3(a: float, radius: float, **kwargs) -> Pattern:
|
|
|
|
"""
|
|
|
|
Generate a masque.Pattern object containing a perturbed L3 cavity.
|
|
|
|
|
2022-11-24 16:12:40 -08:00
|
|
|
Args:
|
|
|
|
a: Lattice constant.
|
|
|
|
radius: Hole radius, in units of a (lattice constant).
|
|
|
|
hole_dose: Dose for all holes. Default 1.
|
|
|
|
trench_dose: Dose for undercut trenches. Default 1.
|
|
|
|
hole_layer: Layer for holes. Default (0, 0).
|
|
|
|
trench_layer: Layer for undercut trenches. Default (1, 0).
|
|
|
|
shifts_a: passed to pcgen.l3_shift
|
|
|
|
shifts_r: passed to pcgen.l3_shift
|
2016-03-30 15:00:00 -07:00
|
|
|
xy_size: [x, y] number of mirror periods in each direction; total size is
|
2022-11-24 16:12:40 -08:00
|
|
|
2 * n + 1 holes in each direction. Default (10, 10).
|
2016-03-30 15:00:00 -07:00
|
|
|
perturbed_radius: radius of holes perturbed to form an upwards-driected beam
|
|
|
|
(multiplicative factor). Default 1.1.
|
|
|
|
trench width: Width of the undercut trenches. Default 1.2e3.
|
2022-11-24 16:12:40 -08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
`masque.Pattern` object containing the L3 design
|
2016-03-30 15:00:00 -07:00
|
|
|
"""
|
|
|
|
|
2022-11-24 16:12:40 -08:00
|
|
|
default_args = {
|
|
|
|
'hole_dose': 1,
|
|
|
|
'trench_dose': 1,
|
|
|
|
'hole_layer': 0,
|
|
|
|
'trench_layer': 1,
|
|
|
|
'shifts_a': (0.15, 0, 0.075),
|
|
|
|
'shifts_r': (1.0, 1.0, 1.0),
|
|
|
|
'xy_size': (10, 10),
|
|
|
|
'perturbed_radius': 1.1,
|
|
|
|
'trench_width': 1.2e3,
|
|
|
|
}
|
2016-03-30 15:00:00 -07:00
|
|
|
kwargs = {**default_args, **kwargs}
|
|
|
|
|
2022-11-24 16:12:40 -08:00
|
|
|
xyr = pcgen.l3_shift_perturbed_defect(
|
|
|
|
mirror_dims=kwargs['xy_size'],
|
|
|
|
perturbed_radius=kwargs['perturbed_radius'],
|
|
|
|
shifts_a=kwargs['shifts_a'],
|
|
|
|
shifts_r=kwargs['shifts_r'],
|
|
|
|
)
|
2016-03-30 15:00:00 -07:00
|
|
|
xyr *= a
|
|
|
|
xyr[:, 2] *= radius
|
|
|
|
|
|
|
|
pat = Pattern()
|
2022-11-24 16:12:40 -08:00
|
|
|
pat.name = f'L3p-a{a:g}r{radius:g}rp{kwargs["perturbed_radius"]:g}'
|
2016-03-30 15:00:00 -07:00
|
|
|
pat.shapes += [shapes.Circle(radius=r, offset=(x, y),
|
|
|
|
dose=kwargs['hole_dose'],
|
|
|
|
layer=kwargs['hole_layer'])
|
|
|
|
for x, y, r in xyr]
|
|
|
|
|
|
|
|
maxes = numpy.max(numpy.fabs(xyr), axis=0)
|
2022-11-24 16:12:40 -08:00
|
|
|
pat.shapes += [
|
|
|
|
shapes.Polygon.rectangle(
|
|
|
|
lx=(2 * maxes[0]),
|
|
|
|
ly=kwargs['trench_width'],
|
|
|
|
offset=(0, s * (maxes[1] + a + kwargs['trench_width'] / 2)),
|
|
|
|
dose=kwargs['trench_dose'],
|
|
|
|
layer=kwargs['trench_layer'],
|
|
|
|
)
|
|
|
|
for s in (-1, 1)]
|
2016-03-30 15:00:00 -07:00
|
|
|
return pat
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2019-08-30 22:13:26 -07:00
|
|
|
max_t = 4000 # number of timesteps
|
2016-03-30 15:00:00 -07:00
|
|
|
|
2016-09-01 14:39:44 -07:00
|
|
|
dx = 25 # discretization (nm/cell)
|
2016-03-30 15:00:00 -07:00
|
|
|
pml_thickness = 8 # (number of cells)
|
|
|
|
|
|
|
|
wl = 1550 # Excitation wavelength and fwhm
|
|
|
|
dwl = 200
|
|
|
|
|
|
|
|
# Device design parameters
|
|
|
|
xy_size = numpy.array([10, 10])
|
|
|
|
a = 430
|
|
|
|
r = 0.285
|
|
|
|
th = 170
|
|
|
|
|
|
|
|
# refractive indices
|
|
|
|
n_slab = 3.408 # InGaAsP(80, 50) @ 1550nm
|
|
|
|
n_air = 1.0 # air
|
|
|
|
|
|
|
|
# Half-dimensions of the simulation grid
|
2019-08-30 22:13:26 -07:00
|
|
|
# xy_max = (xy_size + 1) * a * [1, numpy.sqrt(3)/2]
|
|
|
|
# z_max = 1.6 * a
|
|
|
|
# xyz_max = numpy.hstack((xy_max, z_max)) + pml_thickness * dx
|
|
|
|
#
|
|
|
|
# # Coordinates of the edges of the cells.
|
|
|
|
# # The fdtd package can only do square grids at the moment.
|
|
|
|
# half_edge_coords = [numpy.arange(dx/2, m + dx, step=dx) for m in xyz_max]
|
|
|
|
# edge_coords = [numpy.hstack((-h[::-1], h)) for h in half_edge_coords]
|
|
|
|
edge_coords = [numpy.arange(-100.5, 101), numpy.arange(-1, 1), numpy.arange(-100.5, 101)]
|
|
|
|
# edge_coords = [numpy.arange(-100.5, 101), numpy.arange(-100.5, 101), numpy.arange(-1, 1)]
|
2016-03-30 15:00:00 -07:00
|
|
|
|
|
|
|
# #### Create the grid, mask, and draw the device ####
|
2021-10-31 19:47:25 -07:00
|
|
|
grid = gridlock.Grid(edge_coords)
|
|
|
|
epsilon = grid.allocate(n_air**2)
|
|
|
|
# grid.draw_slab(epsilon,
|
|
|
|
# surface_normal=2,
|
2019-08-30 22:13:26 -07:00
|
|
|
# center=[0, 0, 0],
|
|
|
|
# thickness=th,
|
|
|
|
# eps=n_slab**2)
|
|
|
|
# mask = perturbed_l3(a, r)
|
|
|
|
#
|
2021-10-31 19:47:25 -07:00
|
|
|
# grid.draw_polygons(epsilon,
|
|
|
|
# surface_normal=2,
|
2019-08-30 22:13:26 -07:00
|
|
|
# center=[0, 0, 0],
|
|
|
|
# thickness=2 * th,
|
|
|
|
# eps=n_air**2,
|
|
|
|
# polygons=mask.as_polygons())
|
2016-03-30 15:00:00 -07:00
|
|
|
|
2021-10-31 19:47:48 -07:00
|
|
|
logger.info(f'grid shape: {grid.shape}')
|
2016-03-30 15:00:00 -07:00
|
|
|
# #### Create the simulation grid ####
|
2019-08-30 22:13:26 -07:00
|
|
|
# pmls = [{'axis': a, 'polarity': p, 'thickness': pml_thickness}
|
|
|
|
# for a in 'xyz' for p in 'np']
|
2017-10-01 12:34:11 -07:00
|
|
|
pmls = [{'axis': a, 'polarity': p, 'thickness': pml_thickness}
|
2019-08-30 22:13:26 -07:00
|
|
|
for a in 'xz' for p in 'np']
|
2018-11-30 01:04:00 -08:00
|
|
|
#bloch = [{'axis': a, 'real': 1, 'imag': 0} for a in 'x']
|
|
|
|
bloch = []
|
2021-10-31 19:47:25 -07:00
|
|
|
sim = Simulation(epsilon, do_poynting=True, pmls=pmls, bloch_boundaries=bloch)
|
2016-03-30 15:00:00 -07:00
|
|
|
|
|
|
|
# Source parameters and function
|
|
|
|
w = 2 * numpy.pi * dx / wl
|
|
|
|
fwhm = dwl * w * w / (2 * numpy.pi * dx)
|
|
|
|
alpha = (fwhm ** 2) / 8 * numpy.log(2)
|
|
|
|
delay = 7/numpy.sqrt(2 * alpha)
|
2016-06-21 18:26:16 -07:00
|
|
|
|
|
|
|
def field_source(i):
|
|
|
|
t0 = i * sim.dt - delay
|
|
|
|
return numpy.sin(w * t0) * numpy.exp(-alpha * t0**2)
|
2017-08-14 15:41:20 -07:00
|
|
|
|
2016-09-01 14:39:44 -07:00
|
|
|
with open('sources.c', 'w') as f:
|
|
|
|
f.write(sim.sources['E'])
|
2018-11-30 01:04:00 -08:00
|
|
|
f.write('\n====================H======================\n')
|
2016-09-01 14:39:44 -07:00
|
|
|
f.write(sim.sources['H'])
|
|
|
|
if sim.update_S:
|
2018-11-30 01:04:00 -08:00
|
|
|
f.write('\n=====================S=====================\n')
|
2016-09-01 14:39:44 -07:00
|
|
|
f.write(sim.sources['S'])
|
2018-11-30 01:04:00 -08:00
|
|
|
if bloch:
|
|
|
|
f.write('\n=====================F=====================\n')
|
|
|
|
f.write(sim.sources['F'])
|
|
|
|
f.write('\n=====================G=====================\n')
|
|
|
|
f.write(sim.sources['G'])
|
2016-03-30 15:00:00 -07:00
|
|
|
|
2019-08-30 22:13:26 -07:00
|
|
|
|
|
|
|
planes = numpy.empty((max_t, 4))
|
|
|
|
planes2 = numpy.empty((max_t, 4))
|
|
|
|
Ectr = numpy.empty(max_t)
|
|
|
|
u = numpy.empty(max_t)
|
|
|
|
ui = numpy.empty(max_t)
|
2016-03-30 15:00:00 -07:00
|
|
|
# #### Run a bunch of iterations ####
|
2016-07-16 17:44:51 -07:00
|
|
|
# event = sim.whatever([prev_event]) indicates that sim.whatever should be queued
|
|
|
|
# immediately and run once prev_event is finished.
|
2016-03-30 15:00:00 -07:00
|
|
|
start = time.perf_counter()
|
|
|
|
for t in range(max_t):
|
2018-11-30 01:04:00 -08:00
|
|
|
e = sim.update_E([])
|
2019-08-30 22:13:26 -07:00
|
|
|
# if bloch:
|
|
|
|
# e = sim.update_F([e])
|
2018-11-30 01:04:00 -08:00
|
|
|
e.wait()
|
2016-09-01 14:39:44 -07:00
|
|
|
|
|
|
|
ind = numpy.ravel_multi_index(tuple(grid.shape//2), dims=grid.shape, order='C') + numpy.prod(grid.shape)
|
2019-08-30 22:13:26 -07:00
|
|
|
# sim.E[ind] += field_source(t)
|
|
|
|
if t == 2:
|
|
|
|
sim.E[ind] += 1e6
|
|
|
|
|
|
|
|
h_old = sim.H.copy()
|
|
|
|
|
2016-09-01 14:39:44 -07:00
|
|
|
e = sim.update_H([])
|
2019-08-30 22:13:26 -07:00
|
|
|
# if bloch:
|
|
|
|
# e = sim.update_G([e])
|
2016-09-01 14:39:44 -07:00
|
|
|
e.wait()
|
|
|
|
|
2021-10-31 19:47:25 -07:00
|
|
|
S = sim.S.get().reshape(epsilon.shape) * sim.dt * dx * dx *dx
|
2019-08-30 22:13:26 -07:00
|
|
|
m = 30
|
|
|
|
planes[t] = (
|
|
|
|
S[0][+pml_thickness+2, :, pml_thickness+3:-pml_thickness-3].sum(),
|
|
|
|
S[0][-pml_thickness-2, :, pml_thickness+3:-pml_thickness-3].sum(),
|
|
|
|
S[2][pml_thickness+2:-pml_thickness-2, :, +pml_thickness+2].sum(),
|
|
|
|
S[2][pml_thickness+2:-pml_thickness-2, :, -pml_thickness-2].sum(),
|
|
|
|
)
|
|
|
|
planes2[t] = (
|
|
|
|
S[0][grid.shape[0]//2-1, 0, grid.shape[2]//2].sum(),
|
|
|
|
S[0][grid.shape[0]//2 , 0, grid.shape[2]//2].sum(),
|
|
|
|
S[2][grid.shape[0]//2 , 0, grid.shape[2]//2-1].sum(),
|
|
|
|
S[2][grid.shape[0]//2 , 0, grid.shape[2]//2].sum(),
|
|
|
|
)
|
|
|
|
|
|
|
|
# planes[t] = (
|
|
|
|
# S[0][+pml_thickness+m, pml_thickness+m+1:-pml_thickness-m, :].sum(),
|
|
|
|
# S[0][-pml_thickness-m, pml_thickness+m+1:-pml_thickness-m, :].sum(),
|
|
|
|
# S[1][pml_thickness+1+m:-pml_thickness-m, +pml_thickness+m, :].sum(),
|
|
|
|
# S[1][pml_thickness+1+m:-pml_thickness-m, -pml_thickness-m, :].sum(),
|
|
|
|
# )
|
|
|
|
# planes2[t] = (
|
|
|
|
# S[0][grid.shape[0]//2-1, grid.shape[1]//2 , 0].sum(),
|
|
|
|
# S[0][grid.shape[0]//2 , grid.shape[1]//2 , 0].sum(),
|
|
|
|
# S[1][grid.shape[0]//2 , grid.shape[1]//2-1, 0].sum(),
|
|
|
|
# S[1][grid.shape[0]//2 , grid.shape[1]//2 , 0].sum(),
|
|
|
|
# )
|
|
|
|
Ectr[t] = sim.E[ind].get()
|
|
|
|
u[t] = pyopencl.array.sum(sim.E * sim.E * sim.eps + h_old * sim.H).get() * dx * dx * dx
|
2021-10-31 19:47:25 -07:00
|
|
|
ui[t] = (sim.E * sim.E * sim.eps + h_old * sim.H).reshape(epsilon.shape).get()[:, pml_thickness+m:-pml_thickness-m, :,
|
2024-01-10 20:52:52 -08:00
|
|
|
pml_thickness+m:-pml_thickness-m].sum() * dx * dx * dx
|
2021-10-31 19:47:25 -07:00
|
|
|
# ui[t] = (sim.E * sim.E * sim.eps + h_old * sim.H).reshape(epsilon.shape).get()[:, pml_thickness+m:-pml_thickness-m,
|
2024-01-10 20:52:52 -08:00
|
|
|
# pml_thickness+m:-pml_thickness-m, :].sum() * dx * dx * dx
|
2019-08-30 22:13:26 -07:00
|
|
|
|
2016-09-01 14:39:44 -07:00
|
|
|
if t % 100 == 0:
|
2022-11-24 16:12:40 -08:00
|
|
|
avg = (t + 1) / (time.perf_counter() - start)
|
|
|
|
logger.info(f'iteration {t}: average {avg} iterations per sec')
|
2016-09-01 14:39:44 -07:00
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
with lzma.open('saved_simulation', 'wb') as f:
|
|
|
|
def unvec(f):
|
2021-07-11 17:35:25 -07:00
|
|
|
return meanas.fdmath.unvec(f, grid.shape)
|
2016-09-01 14:39:44 -07:00
|
|
|
d = {
|
|
|
|
'grid': grid,
|
2021-10-31 19:47:25 -07:00
|
|
|
'epsilon': epsilon,
|
2016-09-01 14:39:44 -07:00
|
|
|
'E': unvec(sim.E.get()),
|
|
|
|
'H': unvec(sim.H.get()),
|
2019-08-30 22:13:26 -07:00
|
|
|
'dt': sim.dt,
|
|
|
|
'dx': dx,
|
|
|
|
'planes': planes,
|
|
|
|
'planes2': planes2,
|
|
|
|
'Ectr': Ectr,
|
|
|
|
'u': u,
|
|
|
|
'ui': ui,
|
2016-09-01 14:39:44 -07:00
|
|
|
}
|
|
|
|
if sim.S is not None:
|
|
|
|
d['S'] = unvec(sim.S.get())
|
|
|
|
dill.dump(d, f)
|
2016-03-30 15:00:00 -07:00
|
|
|
|
2017-08-14 15:41:20 -07:00
|
|
|
|
2016-03-30 15:00:00 -07:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|