improve pml specification

This commit is contained in:
jan 2017-10-01 12:34:11 -07:00
parent c137da15b9
commit d02cd18403
4 changed files with 69 additions and 54 deletions

View File

@ -126,7 +126,9 @@ def main():
logger.info('grid shape: {}'.format(grid.shape)) logger.info('grid shape: {}'.format(grid.shape))
# #### Create the simulation grid #### # #### Create the simulation grid ####
sim = Simulation(grid.grids, do_poynting=True, pml_thickness=8) pmls = [{'axis': a, 'polarity': p, 'thickness': pml_thickness}
for a in 'xyz' for p in 'np']
sim = Simulation(grid.grids, do_poynting=True, pmls=pmls)
# Source parameters and function # Source parameters and function
w = 2 * numpy.pi * dx / wl w = 2 * numpy.pi * dx / wl

View File

@ -3,8 +3,8 @@
* *
* Template parameters: * Template parameters:
* common_header: Rendered contents of common.cl * common_header: Rendered contents of common.cl
* pmls: [('x', 'n'), ('z', 'p'),...] list of pml axes and polarities * pmls: [{'axis': 'x', 'polarity': 'n', 'thickness': 8}, ...] list of pml dicts containing
* pml_thickness: Number of cells (integer) axes, polarities, and thicknesses.
* *
* OpenCL args: * OpenCL args:
* E, H, dt, eps, [p{01}e{np}, Psi_{xyz}{np}_E] * E, H, dt, eps, [p{01}e{np}, Psi_{xyz}{np}_E]
@ -18,9 +18,6 @@ __global ftype *epsx = eps + XX;
__global ftype *epsy = eps + YY; __global ftype *epsy = eps + YY;
__global ftype *epsz = eps + ZZ; __global ftype *epsz = eps + ZZ;
{% if pmls -%}
const int pml_thickness = {{pml_thickness}};
{%- endif %}
/* /*
* Precalclate derivatives * Precalclate derivatives
@ -42,7 +39,9 @@ ftype pExi = 0;
ftype pEyi = 0; ftype pEyi = 0;
ftype pEzi = 0; ftype pEzi = 0;
{% for r, p in pmls -%} {% for pml in pmls -%}
{%- set r = pml['axis'] -%}
{%- set p = pml['polarity'] -%}
{%- set u, v = ['x', 'y', 'z'] | reject('equalto', r) -%} {%- set u, v = ['x', 'y', 'z'] | reject('equalto', r) -%}
{%- set psi = 'Psi_' ~ r ~ p ~ '_E' -%} {%- set psi = 'Psi_' ~ r ~ p ~ '_E' -%}
{%- if r != 'y' -%} {%- if r != 'y' -%}
@ -51,14 +50,16 @@ ftype pEzi = 0;
{%- set se, sh = '+', '-' -%} {%- set se, sh = '+', '-' -%}
{%- endif -%} {%- endif -%}
pml_{{r ~ p}}_thickness = {{pml['thickness']}};
{%- if p == 'n' %} {%- if p == 'n' %}
if ( {{r}} < pml_thickness ) { if ( {{r}} < pml_{{r ~ p}_thickness ) {
const size_t ir = {{r}}; // index into pml parameters const size_t ir = {{r}}; // index into pml parameters
{%- elif p == 'p' %} {%- elif p == 'p' %}
if ( s{{r}} > {{r}} && {{r}} >= s{{r}} - pml_thickness ) { if ( s{{r}} > {{r}} && {{r}} >= s{{r}} - pml_{{r ~ p}_thickness ) {
const size_t ir = (s{{r}} - 1) - {{r}}; // index into pml parameters const size_t ir = (s{{r}} - 1) - {{r}}; // index into pml parameters
{%- endif %} {%- endif %}

View File

@ -4,22 +4,18 @@
* *
* Template parameters: * Template parameters:
* common_header: Rendered contents of common.cl * common_header: Rendered contents of common.cl
* pmls: [('x', 'n'), ('z', 'p'),...] list of pml axes and polarities * pmls: [{'axis': 'x', 'polarity': 'n', 'thickness': 8}, ...] list of pml dicts containing
* pml_thickness: Number of cells (integer) axes, polarities, and thicknesses.
* do_poynting: Whether to precalculate poynting vector components (boolean) * do_poynting: Whether to precalculate poynting vector components (boolean)
* *
* OpenCL args: * OpenCL args:
* E, H, dt, [p{01}h{np}, Psi_{xyz}{np}_H], [oS] * E, H, dt, [p{xyz}{01}h{np}, Psi_{xyz}{np}_H], [oS]
*/ */
{{common_header}} {{common_header}}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
{% if pmls -%}
const int pml_thickness = {{pml_thickness}};
{%- endif %}
/* /*
* Precalculate derivatives * Precalculate derivatives
*/ */
@ -57,7 +53,9 @@ ftype pHxi = 0;
ftype pHyi = 0; ftype pHyi = 0;
ftype pHzi = 0; ftype pHzi = 0;
{%- for r, p in pmls -%} {% for pml in pmls -%}
{%- set r = pml['axis'] -%}
{%- set p = pml['polarity'] -%}
{%- set u, v = ['x', 'y', 'z'] | reject('equalto', r) -%} {%- set u, v = ['x', 'y', 'z'] | reject('equalto', r) -%}
{%- set psi = 'Psi_' ~ r ~ p ~ '_H' -%} {%- set psi = 'Psi_' ~ r ~ p ~ '_H' -%}
{%- if r != 'y' -%} {%- if r != 'y' -%}
@ -66,14 +64,16 @@ ftype pHzi = 0;
{%- set se, sh = '+', '-' -%} {%- set se, sh = '+', '-' -%}
{%- endif -%} {%- endif -%}
pml_{{r ~ p}}_thickness = {{pml['thickness']}};
{%- if p == 'n' %} {%- if p == 'n' %}
if ( {{r}} < pml_thickness ) { if ( {{r}} < pml_{{r ~ p}}_thickness ) {
const size_t ir = {{r}}; // index into pml parameters const size_t ir = {{r}}; // index into pml parameters
{%- elif p == 'p' %} {%- elif p == 'p' %}
if ( s{{r}} > {{r}} && {{r}} >= s{{r}} - pml_thickness ) { if ( s{{r}} > {{r}} && {{r}} >= s{{r}} - pml_{{r ~ p}}_thickness ) {
const size_t ir = (s{{r}} - 1) - {{r}}; // index into pml parameters const size_t ir = (s{{r}} - 1) - {{r}}; // index into pml parameters
{%- endif %} {%- endif %}

View File

@ -29,7 +29,8 @@ class Simulation(object):
After constructing this object, call the (update_E, update_H, update_S) members After constructing this object, call the (update_E, update_H, update_S) members
to perform FDTD updates on the stored (E, H, S) fields: to perform FDTD updates on the stored (E, H, S) fields:
sim = Simulation(grid.grids, do_poynting=True, pml_thickness=8) pmls = [{'axis': a, 'polarity': p} for a in 'xyz' for p in 'np']
sim = Simulation(grid.grids, do_poynting=True, pmls=pmls)
with open('sources.c', 'w') as f: with open('sources.c', 'w') as f:
f.write('{}'.format(sim.sources)) f.write('{}'.format(sim.sources))
@ -73,31 +74,34 @@ class Simulation(object):
def __init__(self, def __init__(self,
epsilon: List[numpy.ndarray], epsilon: List[numpy.ndarray],
pmls: List[Dict[str, int or float]],
dt: float = .99/numpy.sqrt(3), dt: float = .99/numpy.sqrt(3),
initial_E: List[numpy.ndarray] = None, initial_E: List[numpy.ndarray] = None,
initial_H: List[numpy.ndarray] = None, initial_H: List[numpy.ndarray] = None,
context: pyopencl.Context = None, context: pyopencl.Context = None,
queue: pyopencl.CommandQueue = None, queue: pyopencl.CommandQueue = None,
float_type: numpy.float32 or numpy.float64 = numpy.float32, float_type: numpy.float32 or numpy.float64 = numpy.float32,
pmls: List[List[str]] = None,
pml_thickness: int = 10,
do_poynting: bool = True): do_poynting: bool = True):
""" """
Initialize the simulation. Initialize the simulation.
:param epsilon: List containing [eps_r,xx, eps_r,yy, eps_r,zz], where each element is a Yee-shifted ndarray :param epsilon: List containing [eps_r,xx, eps_r,yy, eps_r,zz], where each element is a Yee-shifted ndarray
spanning the simulation domain. Relative epsilon is used. spanning the simulation domain. Relative epsilon is used.
:param dt: Time step. Default is the Courant factor. :param pmls: List of dicts with keys:
'axis': One of 'x', 'y', 'z'.
'direction': One of 'n', 'p'.
'thickness': Number of layers, default 8.
'epsilon_eff': Effective epsilon to match to. Default 1.0.
'mu_eff': Effective mu to match to. Default 1.0.
'ln_R_per_layer': Desired (ln(R) / thickness) value. Default -1.6.
'm': Polynomial grading exponent. Default 3.5.
'ma': Exponent for alpha. Default 1.
:param dt: Time step. Default is .99/sqrt(3).
:param initial_E: Initial E-field (default is 0 everywhere). Same format as epsilon. :param initial_E: Initial E-field (default is 0 everywhere). Same format as epsilon.
:param initial_H: Initial H-field (default is 0 everywhere). Same format as epsilon. :param initial_H: Initial H-field (default is 0 everywhere). Same format as epsilon.
:param context: pyOpenCL context. If not given, pyopencl.create_some_context(False) is called. :param context: pyOpenCL context. If not given, pyopencl.create_some_context(False) is called.
:param queue: pyOpenCL command queue. If not given, pyopencl.CommandQueue(context) is called. :param queue: pyOpenCL command queue. If not given, pyopencl.CommandQueue(context) is called.
:param float_type: numpy.float32 or numpy.float64. Default numpy.float32. :param float_type: numpy.float32 or numpy.float64. Default numpy.float32.
:param pmls: List of [axis, direction] pairs which specify simluation boundaries to be
'coated' with a PML (absorbing layer). Axis should be one of 'x', 'y', 'z', and
direction should be one of 'n', 'p' (i.e., negative, positive).
Default is to apply PMLs to all six boundaries.
:param pml_thickness: Thickness of any PMLs, in number of grid cells. Default 10.
:param do_poynting: If true, enables calculation of the poynting vector, S. :param do_poynting: If true, enables calculation of the poynting vector, S.
Poynting vector calculation adds the following computational burdens: Poynting vector calculation adds the following computational burdens:
* During update_H, ~6 extra additions/cell are performed in order to spatially * During update_H, ~6 extra additions/cell are performed in order to spatially
@ -154,8 +158,13 @@ class Simulation(object):
Exception('Initial_H list elements must have same shape as epsilon elements') Exception('Initial_H list elements must have same shape as epsilon elements')
self.H = pyopencl.array.to_device(self.queue, vec(H).astype(float_type)) self.H = pyopencl.array.to_device(self.queue, vec(H).astype(float_type))
if pmls is None: for pml in pmls:
pmls = [[d, p] for d in 'xyz' for p in 'np'] pml.setdefault('thickness', 8)
pml.setdefault('epsilon_eff', 1.0)
pml.setdefault('mu_eff', 1.0)
pml.setdefault('ln_R_per_layer', -1.6)
pml.setdefault('m', 3.5)
pml.setdefault('ma', 1)
ctype = type_to_C(self.arg_type) ctype = type_to_C(self.arg_type)
@ -176,7 +185,6 @@ class Simulation(object):
) )
jinja_args = { jinja_args = {
'common_header': common_source, 'common_header': common_source,
'pml_thickness': pml_thickness,
'pmls': pmls, 'pmls': pmls,
'do_poynting': do_poynting, 'do_poynting': do_poynting,
} }
@ -201,35 +209,39 @@ class Simulation(object):
''' '''
PML PML
''' '''
m = (3.5, 1)
sigma_max = 0.8 * (m[0] + 1) / numpy.sqrt(1.0) # TODO: epsilon_eff (not 1.0)
alpha_max = 0 # TODO: Decide what to do about non-zero alpha
def par(x):
sigma = ((x / pml_thickness) ** m[0]) * sigma_max
alpha = ((1 - x / pml_thickness) ** m[1]) * alpha_max
p0 = numpy.exp(-(sigma + alpha) * dt)
p1 = sigma / (sigma + alpha) * (p0 - 1)
return p0, p1
xen, xep, xhn, xhp = (numpy.arange(1, pml_thickness + 1, dtype=float_type)[::-1] for _ in range(4))
xep -= 0.5
xhn -= 0.5
pml_p_names = [['p' + a + eh + np for np in 'np' for a in '01'] for eh in 'eh']
pml_e_fields = OrderedDict() pml_e_fields = OrderedDict()
pml_h_fields = OrderedDict() pml_h_fields = OrderedDict()
for ne, nh, pe, ph in zip(*pml_p_names, par(xen) + par(xep), par(xhn) + par(xhp)):
pml_e_fields[ptr(ne)] = pyopencl.array.to_device(self.queue, pe)
pml_h_fields[ptr(nh)] = pyopencl.array.to_device(self.queue, ph)
for pml in pmls: for pml in pmls:
uv = 'xyz'.replace(pml[0], '') a = 'xyz'.find(pml['axis'])
psi_base = 'Psi_' + ''.join(pml) + '_'
sigma_max = -pml['ln_R_per_layer'] / 2 * (pml['m'] + 1) / \
numpy.sqrt(pml['epsilon_eff'] * pml['mu_eff'])
alpha_max = 0 # TODO: Nonzero alpha
def par(x):
sigma = ((x / pml['thickness']) ** pml['m']) * sigma_max
alpha = ((1 - x / pml['thickness']) ** pml['ma']) * alpha_max
p0 = numpy.exp(-(sigma + alpha) * dt)
p1 = sigma / (sigma + alpha) * (p0 - 1)
return p0, p1
xe, xh = (numpy.arange(1, pml['thickness'] + 1, dtype=float_type)[::-1] for _ in range(2))
if pml['polarity'] == 'p':
xe -= 0.5
elif pml['polarity'] == 'n':
xh -= 0.5
pml_p_names = [['p' + pml['axis'] + i + eh + pml['polarity'] for i in '01'] for eh in 'eh']
for name_e, name_h, pe, ph in zip(pml_p_names[0], pml_p_names[1], par(xe), par(xh)):
pml_e_fields[ptr(name_e)] = pyopencl.array.to_device(self.queue, pe)
pml_h_fields[ptr(name_h)] = pyopencl.array.to_device(self.queue, ph)
uv = 'xyz'.replace(pml['axis'], '')
psi_base = 'Psi_' + pml['axis'] + pml['polarity'] + '_'
psi_names = [[psi_base + eh + c for c in uv] for eh in 'EH'] psi_names = [[psi_base + eh + c for c in uv] for eh in 'EH']
psi_shape = list(epsilon[0].shape) psi_shape = list(epsilon[0].shape)
psi_shape['xyz'.find(pml[0])] = pml_thickness psi_shape[a] = pml['thickness']
for ne, nh in zip(*psi_names): for ne, nh in zip(*psi_names):
pml_e_fields[ptr(ne)] = pyopencl.array.zeros(self.queue, tuple(psi_shape), dtype=self.arg_type) pml_e_fields[ptr(ne)] = pyopencl.array.zeros(self.queue, tuple(psi_shape), dtype=self.arg_type)