Skip to content

Commit

Permalink
eigenmodes of diffracted planewaves (#1316)
Browse files Browse the repository at this point in the history
* eigenmodes of diffracted planewaves in 3d

* overload get_eigenmode_coefficients_and_kpoints with diffractedplanewave object in place of bands array

* SWIG typemap for axis member of class diffractedplanewave

* only set kperp when computing diffracted orders and fix get_eigenmode header for non-MPB builds

* add missing header for get_eigenmode_coefficients for non-MPB build

* typo

* SWIG typemap for diffraction order input parameter g

* rename parameter s to spol to resolve name conflict with Scheme intersect_ray_with_segment argument

* move constructor for diffractedplanewave from mpb.cpp to sources.cpp

* bug fix in k2sum and add Python test

* remove unicode character from Python test

* bands parameter of get_eigenmode_coefficients can only be either a list or DiffractedPlanewave object

* use exception handling for isinstance since Python2 does not recognize range as separate type

* eigenvalue is square if eigenfrequency (bug fix for group velocity)

* remove output for debugging
  • Loading branch information
oskooi authored Aug 26, 2020
1 parent 64d509e commit 6bed831
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 35 deletions.
5 changes: 4 additions & 1 deletion python/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ endif # WITH_MPI

if WITH_MPB
BINARY_GRATING_TEST = $(TEST_DIR)/binary_grating.py
DIFFRACTED_PLANEWAVE_TEST = $(TEST_DIR)/diffracted_planewave.py
DISPERSIVE_EIGENMODE_TEST = $(TEST_DIR)/dispersive_eigenmode.py
KDOM_TEST = $(TEST_DIR)/kdom.py
MODE_COEFFS_TEST = $(TEST_DIR)/mode_coeffs.py
MODE_DECOMPOSITION_TEST = $(TEST_DIR)/mode_decomposition.py
WVG_SRC_TEST = $(TEST_DIR)/wvg_src.py
else
BINARY_GRATING_TEST =
DIFFRACTED_PLANEWAVE_TEST =
DISPERSIVE_EIGENMODE_TEST =
KDOM_TEST =
MODE_COEFFS_TEST =
Expand All @@ -43,9 +45,10 @@ TESTS = \
$(TEST_DIR)/cavity_farfield.py \
$(TEST_DIR)/chunks.py \
$(TEST_DIR)/cyl_ellipsoid.py \
${DISPERSIVE_EIGENMODE_TEST} \
$(TEST_DIR)/dft_energy.py \
$(TEST_DIR)/dft_fields.py \
${DIFFRACTED_PLANEWAVE_TEST} \
${DISPERSIVE_EIGENMODE_TEST} \
$(TEST_DIR)/divide_mpi_processes.py \
$(TEST_DIR)/eigfreq.py \
$(TEST_DIR)/faraday_rotation.py \
Expand Down
34 changes: 34 additions & 0 deletions python/meep.i
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,23 @@ kpoint_list get_eigenmode_coefficients_and_kpoints(meep::fields *f, meep::dft_fl
return res;
}

kpoint_list get_eigenmode_coefficients_and_kpoints(meep::fields *f, meep::dft_flux flux, const meep::volume &eig_vol,
meep::diffractedplanewave dp, int parity, double eig_resolution,
double eigensolver_tol, std::complex<double> *coeffs,
double *vgrp, meep::kpoint_func user_kpoint_func,
void *user_kpoint_data, double *cscale, meep::direction d) {

size_t num_kpoints = flux.freq.size();
meep::vec *kpoints = new meep::vec[num_kpoints];
meep::vec *kdom = new meep::vec[num_kpoints];
f->get_eigenmode_coefficients(flux, eig_vol, NULL, 1, parity, eig_resolution, eigensolver_tol,
coeffs, vgrp, user_kpoint_func, user_kpoint_data, kpoints, kdom, cscale, d, &dp);

kpoint_list res = {kpoints, num_kpoints, kdom, num_kpoints};

return res;
}

PyObject *_get_array_slice_dimensions(meep::fields *f, const meep::volume &where, size_t dims[3],
bool collapse_empty_dimensions, bool snap_empty_dimensions,
meep::component cgrid = Centered, PyObject *min_max_loc = NULL) {
Expand Down Expand Up @@ -1053,6 +1070,16 @@ void _get_gradient(PyObject *grad, PyObject *fields_a, PyObject *fields_f, PyObj
$1 = (std::complex<double> *)array_data($input);
}

// typemaps for diffractedplanewave

%typecheck(SWIG_TYPECHECK_POINTER, fragment="NumPy_Fragments") double axis[3] {
$1 = is_array($input);
}

%typemap(in) double axis[3] {
$1 = (double *)array_data($input);
}

// typemaps for gaussianbeam

%typecheck(SWIG_TYPECHECK_POINTER, fragment="NumPy_Fragments") std::complex<double> E0[3] {
Expand Down Expand Up @@ -1495,6 +1522,12 @@ kpoint_list get_eigenmode_coefficients_and_kpoints(meep::fields *f, meep::dft_fl
std::complex<double> *coeffs, double *vgrp,
meep::kpoint_func user_kpoint_func, void *user_kpoint_data,
double *cscale, meep::direction d);
kpoint_list get_eigenmode_coefficients_and_kpoints(meep::fields *f, meep::dft_flux flux,
const meep::volume &eig_vol, meep::diffractedplanewave dp,
int parity, double eig_resolution, double eigensolver_tol,
std::complex<double> *coeffs, double *vgrp,
meep::kpoint_func user_kpoint_func, void *user_kpoint_data,
double *cscale, meep::direction d);
PyObject *_get_array_slice_dimensions(meep::fields *f, const meep::volume &where, size_t dims[3],
bool collapse_empty_dimensions, bool snap_empty_dimensions,
meep::component cgrid = Centered, PyObject *min_max_loc = NULL);
Expand Down Expand Up @@ -1581,6 +1614,7 @@ PyObject *_get_array_slice_dimensions(meep::fields *f, const meep::volume &where
DftEnergy,
DftFields,
Volume,
DiffractedPlanewave,
after_sources,
after_sources_and_time,
after_time,
Expand Down
99 changes: 80 additions & 19 deletions python/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,32 @@ def py_v3_to_vec(dims, iterable, is_cylindrical=False):
else:
raise ValueError("Invalid dimensions in Volume: {}".format(dims))

class DiffractedPlanewave(object):
def __init__(self,
g=None,
axis=None,
s=None,
p=None):
self._g = g
self._axis = Vector3(*axis)
self._s = complex(s)
self._p = complex(p)

@property
def g(self):
return self._g

@property
def axis(self):
return self._axis

@property
def s(self):
return self._s

@property
def p(self):
return self._p

class PML(object):
"""
Expand Down Expand Up @@ -3257,25 +3283,60 @@ def get_eigenmode_coefficients(self, flux, bands, eig_parity=mp.NO_PARITY, eig_v
if direction is None or direction == mp.AUTOMATIC:
direction = flux.normal_direction

num_bands = len(bands)
coeffs = np.zeros(2 * num_bands * flux.freq.size(), dtype=np.complex128)
vgrp = np.zeros(num_bands * flux.freq.size())
cscale = np.zeros(num_bands * flux.freq.size())

kpoints, kdom = mp.get_eigenmode_coefficients_and_kpoints(
self.fields,
flux.swigobj,
eig_vol,
np.array(bands, dtype=np.intc),
eig_parity,
eig_resolution,
eig_tolerance,
coeffs,
vgrp,
kpoint_func,
cscale,
direction
)
try:
bands_list_range = isinstance(bands, (list,range))
except TypeError:
bands_list_range = isinstance(bands, list)

if bands_list_range:
num_bands = len(bands)
coeffs = np.zeros(2 * num_bands * flux.freq.size(), dtype=np.complex128)
vgrp = np.zeros(num_bands * flux.freq.size())
cscale = np.zeros(num_bands * flux.freq.size())

kpoints, kdom = mp.get_eigenmode_coefficients_and_kpoints(
self.fields,
flux.swigobj,
eig_vol,
np.array(bands, dtype=np.intc),
eig_parity,
eig_resolution,
eig_tolerance,
coeffs,
vgrp,
kpoint_func,
cscale,
direction
)
elif isinstance(bands, DiffractedPlanewave):
num_bands = 1
coeffs = np.zeros(2 * num_bands * flux.freq.size(), dtype=np.complex128)
vgrp = np.zeros(num_bands * flux.freq.size())
cscale = np.zeros(num_bands * flux.freq.size())
diffractedplanewave_args = [
np.array(bands.g, dtype=np.intc),
np.array([bands.axis.x, bands.axis.y, bands.axis.z], dtype=np.float64),
bands.s * 1.0,
bands.p * 1.0
]
diffractedplanewave = mp.diffractedplanewave(*diffractedplanewave_args)

kpoints, kdom = mp.get_eigenmode_coefficients_and_kpoints(
self.fields,
flux.swigobj,
eig_vol,
diffractedplanewave,
eig_parity,
eig_resolution,
eig_tolerance,
coeffs,
vgrp,
kpoint_func,
cscale,
direction
)
else:
raise TypeError("get_eigenmode_coefficients: bands must be either a list or DiffractedPlanewave object")

return EigCoeffsResult(np.reshape(coeffs, (num_bands, flux.freq.size(), 2)), vgrp, kpoints, kdom, cscale)

Expand Down
124 changes: 124 additions & 0 deletions python/tests/diffracted_planewave.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from __future__ import division

import unittest
import meep as mp
import math
import cmath
import numpy as np

class TestDiffractedPlanewave(unittest.TestCase):

def run_binary_grating_diffraction(self, gp, gh, gdc, theta, bands, orders):

resolution = 25 # pixels/um

dpml = 1.0 # PML thickness
dsub = 3.0 # substrate thickness
dpad = 3.0 # length of padding between grating and PML

sx = dpml+dsub+gh+dpad+dpml
sy = gp

cell_size = mp.Vector3(sx,sy,0)
absorber_layers = [mp.Absorber(thickness=dpml,direction=mp.X)]

wvl = 0.5 # center wavelength
fcen = 1/wvl # center frequency
df = 0.05*fcen # frequency width

ng = 1.5
glass = mp.Medium(index=ng)

# rotation angle of incident planewave; counter clockwise (CCW) about Z axis, 0 degrees along +X axis
theta_in = math.radians(theta)

eig_parity = mp.ODD_Z

# k (in source medium) with correct length (plane of incidence: XY)
k = mp.Vector3(fcen*ng).rotate(mp.Vector3(z=1), theta_in)

if theta_in == 0:
k = mp.Vector3()
eig_parity += mp.EVEN_Y

def pw_amp(k,x0):
def _pw_amp(x):
return cmath.exp(1j*2*math.pi*k.dot(x+x0))
return _pw_amp

src_pt = mp.Vector3(-0.5*sx+dpml,0,0)
sources = [mp.Source(mp.GaussianSource(fcen,fwidth=df),
component=mp.Ez,
center=src_pt,
size=mp.Vector3(0,sy,0),
amp_func=pw_amp(k,src_pt))]

sim = mp.Simulation(resolution=resolution,
cell_size=cell_size,
boundary_layers=absorber_layers,
k_point=k,
default_material=glass,
sources=sources)

tran_pt = mp.Vector3(0.5*sx-dpml,0,0)
tran_flux = sim.add_flux(fcen, 0, 1,
mp.FluxRegion(center=tran_pt, size=mp.Vector3(0,sy,0)))

sim.run(until_after_sources=50)

input_flux = mp.get_fluxes(tran_flux)

sim.reset_meep()

geometry = [mp.Block(material=glass,
size=mp.Vector3(dpml+dsub,mp.inf,mp.inf),
center=mp.Vector3(-0.5*sx+0.5*(dpml+dsub),0,0)),
mp.Block(material=glass,
size=mp.Vector3(gh,gdc*gp,mp.inf),
center=mp.Vector3(-0.5*sx+dpml+dsub+0.5*gh,0,0))]

sim = mp.Simulation(resolution=resolution,
cell_size=cell_size,
boundary_layers=absorber_layers,
geometry=geometry,
k_point=k,
sources=sources)

tran_flux = sim.add_mode_monitor(fcen, 0, 1,
mp.FluxRegion(center=tran_pt, size=mp.Vector3(0,sy,0)))

sim.run(until_after_sources=100)

for band,order in zip(bands,orders):
res = sim.get_eigenmode_coefficients(tran_flux,
[band],
eig_parity=eig_parity)
tran_ref = abs(res.alpha[0,0,0])**2/input_flux[0]
if (theta_in == 0):
tran_ref = 0.5*tran_ref
vg_ref = res.vgrp[0]

res = sim.get_eigenmode_coefficients(tran_flux,
mp.DiffractedPlanewave((0,order,0),mp.Vector3(0,1,0),1,0))
if res is not None:
tran_dp = abs(res.alpha[0,0,0])**2/input_flux[0]
if ((theta_in == 0) and (order == 0)):
tran_dp = 0.5*tran_dp
else:
tran_dp = 0
vg_dp = res.vgrp[0]

err = abs(tran_ref-tran_dp)/tran_ref
print("tran:, {} (band), {} (order), {:.8f} (eigensolver), {:.8f} (planewave), {:.8f} (error)".format(band,order,tran_ref,tran_dp,err))

self.assertAlmostEqual(vg_ref,vg_dp,places=5)
self.assertAlmostEqual(tran_ref,tran_dp,places=5)

def test_diffracted_planewave(self):
self.run_binary_grating_diffraction(2.6,0.4,0.6,0,range(1,6),range(0,5))
self.run_binary_grating_diffraction(2.6,0.4,0.6,13.4,range(1,6),[-2,-1,-3,0,-4])
self.run_binary_grating_diffraction(10.0,0.5,0.5,0,[2,4,6],[1,3,5])
self.run_binary_grating_diffraction(10.0,0.5,0.5,10.7,[1,4,8],[-6,-4,-2])

if __name__ == '__main__':
unittest.main()
24 changes: 21 additions & 3 deletions src/meep.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,22 @@ class chunkloop_field_components {
std::vector<std::complex<double> > values; // updated by update_values(idx)
};

class diffractedplanewave {

public:
diffractedplanewave(int g[3], double axis[3], std::complex<double> s, std::complex<double> p);
int *get_g() { return g; };
double *get_axis() { return axis; }
std::complex<double> get_s() const { return s; };
std::complex<double> get_p() const { return p; };

private:
int g[3]; // diffraction order
double axis[3]; // axis vector
std::complex<double> s; // s polarization amplitude
std::complex<double> p; // p polarization ampiltude
};

class gaussianbeam {

public:
Expand Down Expand Up @@ -1710,7 +1726,7 @@ class fields {
void *get_eigenmode(double frequency, direction d, const volume where, const volume eig_vol,
int band_num, const vec &kpoint, bool match_frequency, int parity,
double resolution, double eigensolver_tol, double *kdom = 0,
void **user_mdata = 0);
void **user_mdata = 0, diffractedplanewave *dp = 0);

void add_eigenmode_source(component c, const src_time &src, direction d, const volume &where,
const volume &eig_vol, int band_num, const vec &kpoint,
Expand All @@ -1722,12 +1738,14 @@ class fields {
int parity, double eig_resolution, double eigensolver_tol,
std::complex<double> *coeffs, double *vgrp,
kpoint_func user_kpoint_func, void *user_kpoint_data,
vec *kpoints, vec *kdom, double *cscale, direction d);
vec *kpoints, vec *kdom, double *cscale, direction d,
diffractedplanewave *dp = 0);
void get_eigenmode_coefficients(dft_flux flux, const volume &eig_vol, int *bands, int num_bands,
int parity, double eig_resolution, double eigensolver_tol,
std::complex<double> *coeffs, double *vgrp,
kpoint_func user_kpoint_func = 0, void *user_kpoint_data = 0,
vec *kpoints = 0, vec *kdom = 0, double *cscale = 0);
vec *kpoints = 0, vec *kdom = 0, double *cscale = 0,
diffractedplanewave *dp = 0);

// initialize.cpp:
void initialize_field(component, std::complex<double> f(const vec &));
Expand Down
Loading

0 comments on commit 6bed831

Please sign in to comment.