You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
opencl_fdtd/fdtd.py

265 lines
9.2 KiB
Python

8 years ago
"""
Example code for running an OpenCL FDTD simulation
See main() for simulation setup.
"""
import sys
import time
import logging
5 years ago
import pyopencl
8 years ago
import numpy
7 years ago
import lzma
import dill
8 years ago
7 years ago
from opencl_fdtd import Simulation
8 years ago
from masque import Pattern, shapes
import gridlock
import pcgen
4 years ago
import meanas
__author__ = 'Jan Petykiewicz'
8 years ago
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
8 years ago
def perturbed_l3(a: float, radius: float, **kwargs) -> Pattern:
"""
Generate a masque.Pattern object containing a perturbed L3 cavity.
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
8 years ago
xy_size: [x, y] number of mirror periods in each direction; total size is
2 * n + 1 holes in each direction. Default (10, 10).
8 years ago
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.
Returns:
`masque.Pattern` object containing the L3 design
8 years ago
"""
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,
}
8 years ago
kwargs = {**default_args, **kwargs}
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'],
)
8 years ago
xyr *= a
xyr[:, 2] *= radius
pat = Pattern()
pat.name = f'L3p-a{a:g}r{radius:g}rp{kwargs["perturbed_radius"]:g}'
8 years ago
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)
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)]
8 years ago
return pat
def main():
5 years ago
max_t = 4000 # number of timesteps
8 years ago
dx = 25 # discretization (nm/cell)
8 years ago
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
5 years ago
# 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)]
8 years ago
# #### Create the grid, mask, and draw the device ####
grid = gridlock.Grid(edge_coords)
epsilon = grid.allocate(n_air**2)
# grid.draw_slab(epsilon,
# surface_normal=2,
5 years ago
# center=[0, 0, 0],
# thickness=th,
# eps=n_slab**2)
# mask = perturbed_l3(a, r)
#
# grid.draw_polygons(epsilon,
# surface_normal=2,
5 years ago
# center=[0, 0, 0],
# thickness=2 * th,
# eps=n_air**2,
# polygons=mask.as_polygons())
8 years ago
3 years ago
logger.info(f'grid shape: {grid.shape}')
8 years ago
# #### Create the simulation grid ####
5 years ago
# pmls = [{'axis': a, 'polarity': p, 'thickness': pml_thickness}
# for a in 'xyz' for p in 'np']
pmls = [{'axis': a, 'polarity': p, 'thickness': pml_thickness}
5 years ago
for a in 'xz' for p in 'np']
#bloch = [{'axis': a, 'real': 1, 'imag': 0} for a in 'x']
bloch = []
sim = Simulation(epsilon, do_poynting=True, pmls=pmls, bloch_boundaries=bloch)
8 years ago
# 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)
def field_source(i):
t0 = i * sim.dt - delay
return numpy.sin(w * t0) * numpy.exp(-alpha * t0**2)
7 years ago
with open('sources.c', 'w') as f:
f.write(sim.sources['E'])
f.write('\n====================H======================\n')
f.write(sim.sources['H'])
if sim.update_S:
f.write('\n=====================S=====================\n')
f.write(sim.sources['S'])
if bloch:
f.write('\n=====================F=====================\n')
f.write(sim.sources['F'])
f.write('\n=====================G=====================\n')
f.write(sim.sources['G'])
8 years ago
5 years ago
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)
8 years ago
# #### Run a bunch of iterations ####
# event = sim.whatever([prev_event]) indicates that sim.whatever should be queued
# immediately and run once prev_event is finished.
8 years ago
start = time.perf_counter()
for t in range(max_t):
e = sim.update_E([])
5 years ago
# if bloch:
# e = sim.update_F([e])
e.wait()
ind = numpy.ravel_multi_index(tuple(grid.shape//2), dims=grid.shape, order='C') + numpy.prod(grid.shape)
5 years ago
# sim.E[ind] += field_source(t)
if t == 2:
sim.E[ind] += 1e6
h_old = sim.H.copy()
e = sim.update_H([])
5 years ago
# if bloch:
# e = sim.update_G([e])
e.wait()
S = sim.S.get().reshape(epsilon.shape) * sim.dt * dx * dx *dx
5 years ago
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
ui[t] = (sim.E * sim.E * sim.eps + h_old * sim.H).reshape(epsilon.shape).get()[:, pml_thickness+m:-pml_thickness-m, :,
4 months ago
pml_thickness+m:-pml_thickness-m].sum() * dx * dx * dx
# ui[t] = (sim.E * sim.E * sim.eps + h_old * sim.H).reshape(epsilon.shape).get()[:, pml_thickness+m:-pml_thickness-m,
4 months ago
# pml_thickness+m:-pml_thickness-m, :].sum() * dx * dx * dx
5 years ago
if t % 100 == 0:
avg = (t + 1) / (time.perf_counter() - start)
logger.info(f'iteration {t}: average {avg} iterations per sec')
sys.stdout.flush()
with lzma.open('saved_simulation', 'wb') as f:
def unvec(f):
return meanas.fdmath.unvec(f, grid.shape)
d = {
'grid': grid,
'epsilon': epsilon,
'E': unvec(sim.E.get()),
'H': unvec(sim.H.get()),
5 years ago
'dt': sim.dt,
'dx': dx,
'planes': planes,
'planes2': planes2,
'Ectr': Ectr,
'u': u,
'ui': ui,
}
if sim.S is not None:
d['S'] = unvec(sim.S.get())
dill.dump(d, f)
8 years ago
7 years ago
8 years ago
if __name__ == '__main__':
main()