diff --git a/contrib/maxima/maxwellrectcav3d.mac b/contrib/maxima/maxwellrectcav3d.mac
new file mode 100644
index 0000000000000000000000000000000000000000..abda9687d244b25b3cf546bea9f83383e222bc34
--- /dev/null
+++ b/contrib/maxima/maxwellrectcav3d.mac
@@ -0,0 +1,73 @@
+kill(all);
+load("eigen");
+load("itensor");
+load("diag");
+
+/* -------------------------------------------------------------------------- */
+/* Utilities */
+/* -------------------------------------------------------------------------- */
+mu: 1;
+epsilon: 1;
+
+coords:[x,y,z];
+
+curl(x):=crossfunc(lambda([i,j], diff(x[j], coords[i])));
+div(f):=sum(diff(f[j], coords[j]), j, 1, length(coords));
+
+faraday(max_E, max_H):= curl(max_E) - %i * omega * max_H;
+ampere(max_E, max_H):= curl(max_H) + %i * omega * mu * epsilon * max_E;
+/*
+ampere(max_E, max_H):= curl(max_H) + %i * omega * max_E;
+*/
+
+
+crossfunc(f):=makelist(
+ sum(sum(
+ levi_civita([i,j,k])*f(j,k),
+ j,1,3),k,1,3),i,1,3)$
+
+crossprod(a,b):=crossfunc(lambda([j,k], a[j]*b[k]));
+
+/* -------------------------------------------------------------------------- */
+/* Attempts */
+/* -------------------------------------------------------------------------- */
+nabla_t_squared(f):=diff(f, x, 2) + diff(f, y, 2);
+nabla_t(f):= [diff(f, x, 1) , diff(f, y, 1), 0];
+
+/*
+gamma : sqrt((k_y^2 + k_x^2)/ (mu *epsilon)) ;
+*/
+omega : sqrt(k_x^2 + k_y^2 + k_z^2);
+gamma : sqrt((k_y^2 + k_x^2)) ;
+
+psi_cand:E_0*sin(k_x*x)*sin(k_y*y);
+
+
+
+wave_eqn(f, gam_s):=nabla_t_squared(f) + mu*epsilon*gam_s*f;
+gam_s : gamma^2;
+
+/* The _t indicates transverse components (i.e. x and y components only) */
+/*
+E_t(psi):=(-k_z/gam_s)*sin(k_z * z)* nabla_t(psi);
+H_t(psi):=crossprod((%i * omega /gam_s)*cos(k_z * z)* [0,0,1], nabla_t(psi));
+*/
+E_t(psi):=(-k_z/gam_s)*sin(k_z * z)* nabla_t(psi);
+H_t(psi):=crossprod((%i * omega * epsilon/gam_s)*cos(k_z * z)* [0,0,1], nabla_t(psi));
+
+
+/* These are used as the analytic solution for a rectangular cavity resonator
+ with travsverse magnetic waves */
+
+E : E_t(psi_cand) + [0,0,psi_cand * cos(k_z * z)];
+H :H_t(psi_cand);
+
+Etime : E * %e ^ (- %i * omega * t);
+Htime : H * %e ^ (- %i * omega * t);
+
+
+trigrat(div(E));
+trigrat(div(H));
+trigrat(faraday(E,H));
+trigrat(ampere(E,H));
+
diff --git a/examples/maxwell/analytic_solutions.py b/examples/maxwell/analytic_solutions.py
deleted file mode 100644
index 7ff8884f1d19e46d320e0cc7a8c5301f74d14551..0000000000000000000000000000000000000000
--- a/examples/maxwell/analytic_solutions.py
+++ /dev/null
@@ -1,460 +0,0 @@
-# Copyright (C) 2007 Andreas Kloeckner
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-
-
-from __future__ import division
-from __future__ import absolute_import
-from __future__ import print_function
-from grudge.tools import \
- cyl_bessel_j, \
- cyl_bessel_j_prime
-from math import sqrt, pi, sin, cos, atan2, acos
-import numpy
-import numpy.linalg as la
-import cmath
-from six.moves import range
-from six.moves import zip
-
-
-
-
-# solution adapters -----------------------------------------------------------
-class RealPartAdapter:
- def __init__(self, adaptee):
- self.adaptee = adaptee
-
- @property
- def shape(self):
- return self.adaptee.shape
-
- def __call__(self, x, el):
- return [xi.real for xi in self.adaptee(x, el)]
-
-class SplitComplexAdapter:
- def __init__(self, adaptee):
- self.adaptee = adaptee
-
- @property
- def shape(self):
- (n,) = self.adaptee.shape
- return (n*2,)
-
- def __call__(self, x, el):
- ad_x = self.adaptee(x, el)
- return [xi.real for xi in ad_x] + [xi.imag for xi in ad_x]
-
-
-
-
-class CylindricalFieldAdapter:
- """Turns a field returned as (r, phi, z) components into
- (x,y,z) components.
- """
- def __init__(self, adaptee):
- self.adaptee = adaptee
-
- @property
- def shape(self):
- return self.adaptee.shape
-
- def __call__(self, x, el):
- xy = x[:2]
- r = la.norm(xy)
- phi = atan2(x[1], x[0])
-
- prev_result = self.adaptee(x, el)
- result = []
- i = 0
- while i < len(prev_result):
- fr, fphi, fz = prev_result[i:i+3]
- result.extend([
- cos(phi)*fr - sin(phi)*fphi, # ex
- sin(phi)*fr + cos(phi)*fphi, # ey
- fz,
- ])
- i += 3
-
- return result
-
-
-
-
-class SphericalFieldAdapter:
- """Turns the adaptee's field C{(Er, Etheta, Ephi)} components into
- C{(Ex,Ey,Ez)} components.
-
- Conventions are those of
- http://mathworld.wolfram.com/SphericalCoordinates.html
- """
- def __init__(self, adaptee):
- self.adaptee = adaptee
-
- @property
- def shape(self):
- return self.adaptee.shape
-
- @staticmethod
- def r_theta_phi_from_xyz(x):
- r = la.norm(x)
- return r, atan2(x[1], x[0]), acos(x[2]/r)
-
- def __call__(self, x, el):
- r, theta, phi = self.r_theta_phi_from_xyz(x)
-
- er = numpy.array([
- cos(theta)*sin(phi),
- sin(theta)*sin(phi),
- cos(phi)])
- etheta = numpy.array([
- -sin(theta),
- cos(theta),
- 0])
- ephi = numpy.array([
- cos(theta)*cos(phi),
- sin(theta)*cos(phi),
- -sin(phi)])
-
- adaptee_result = self.adaptee(x, el)
- result = numpy.empty_like(adaptee_result)
- i = 0
- while i < len(adaptee_result):
- fr, ftheta, fphi = adaptee_result[i:i+3]
- result[i:i+3] = fr*er + ftheta*etheta + fphi*ephi
- i += 3
-
- return result
-
-
-
-
-# actual solutions ------------------------------------------------------------
-class CylindricalCavityMode:
- """A cylindrical TM cavity mode.
-
- Taken from:
- J.D. Jackson, Classical Electrodynamics, Wiley.
- 3rd edition, 2001.
- ch 8.7, p. 368f.
- """
-
- def __init__(self, m, n, p, radius, height, epsilon, mu):
- try:
- from bessel_zeros import bessel_zeros
- except ImportError:
- print("*** You need to generate the bessel root data file.")
- print("*** Execute generate-bessel-zeros.py at the command line.")
- raise
-
- assert m >= 0 and m == int(m)
- assert n >= 1 and n == int(n)
- assert p >= 0 and p == int(p)
- self.m = m
- self.n = n
- self.p = p
- self.phi_sign = 1
-
- R = self.radius = radius
- d = self.height = height
-
- self.epsilon = epsilon
- self.mu = mu
-
- self.t = 0
-
- x_mn = bessel_zeros[m][n-1]
-
- self.omega = 1 / sqrt(mu*epsilon) * sqrt(
- x_mn**2 / R**2
- + p**2 * pi**2 / d**2)
-
- self.gamma_mn = x_mn/R
-
- def set_time(self, t):
- self.t = t
-
- shape = (6,)
-
- def __call__(self, x, el):
- # coordinates -----------------------------------------------------
- xy = x[:2]
- r = sqrt(xy*xy)
- phi = atan2(x[1], x[0])
- z = x[2]
-
- # copy instance variables for easier access -----------------------
- m = self.m
- p = self.p
- gamma = self.gamma_mn
- phi_sign = self.phi_sign
- omega = self.omega
- d = self.height
- epsilon = self.epsilon
-
- # common subexpressions -------------------------------------------
- tdep = cmath.exp(-1j * omega * self.t)
- phi_factor = cmath.exp(phi_sign * 1j * m * phi)
-
- # psi and derivatives ---------------------------------------------
- psi = cyl_bessel_j(m, gamma * r) * phi_factor
- psi_dr = gamma*cyl_bessel_j_prime(m, gamma*r) * phi_factor
- psi_dphi = (cyl_bessel_j(m, gamma * r)
- * 1/r * phi_sign*1j*m * phi_factor)
-
- # field components in polar coordinates ---------------------------
- ez = tdep * cos(p * pi * z / d) * psi
-
- e_transverse_factor = (tdep
- * (-p*pi/(d*gamma**2))
- * sin(p * pi * z / d))
-
- er = e_transverse_factor * psi_dr
- ephi = e_transverse_factor * psi_dphi
-
- hz = 0j
-
- # z x grad psi = z x (psi_x, psi_y) = (-psi_y, psi_x)
- # z x grad psi = z x (psi_r, psi_phi) = (-psi_phi, psi_r)
- h_transverse_factor = (tdep
- * 1j*epsilon*omega/gamma**2
- * cos(p * pi * z / d))
-
- hr = h_transverse_factor * (-psi_dphi)
- hphi = h_transverse_factor * psi_dr
-
- return [er, ephi, ez, hr, hphi, hz]
-
-
-
-
-class RectangularWaveguideMode:
- """A rectangular TM cavity mode."""
-
- def __init__(self, epsilon, mu, mode_indices,
- dimensions=(1,1,1), coefficients=(1,0,0),
- forward_coeff=1, backward_coeff=0):
- for n in mode_indices:
- assert n >= 0 and n == int(n)
- self.mode_indices = mode_indices
- self.dimensions = dimensions
- self.coefficients = coefficients
- self.forward_coeff = forward_coeff
- self.backward_coeff = backward_coeff
-
- self.epsilon = epsilon
- self.mu = mu
-
- self.t = 0
-
- self.factors = [n*pi/a for n, a in zip(self.mode_indices, self.dimensions)]
-
- c = 1/sqrt(mu*epsilon)
- self.k = sqrt(sum(f**2 for f in self.factors))
- self.omega = self.k*c
-
- def set_time(self, t):
- self.t = t
-
- def __call__(self, discr):
- f,g,h = self.factors
- omega = self.omega
- k = self.k
-
- x = discr.nodes[:,0]
- y = discr.nodes[:,1]
- if discr.dimensions > 2:
- z = discr.nodes[:,2]
- else:
- z = discr.volume_zeros()
-
- sx = numpy.sin(f*x)
- sy = numpy.sin(g*y)
- cx = numpy.cos(f*x)
- cy = numpy.cos(g*y)
-
- zdep_add = numpy.exp(1j*h*z)+numpy.exp(-1j*h*z)
- zdep_sub = numpy.exp(1j*h*z)-numpy.exp(-1j*h*z)
-
- tdep = numpy.exp(-1j * omega * self.t)
-
- C = 1j/(f**2+g**2)
-
- result = numpy.zeros((6,len(x)), dtype=zdep_add.dtype)
-
- result[0] = C*f*h*cx*sy*zdep_sub*tdep # ex
- result[1] = C*g*h*sx*cy*zdep_sub*tdep # ey
- result[2] = sx*sy*zdep_add*tdep # ez
- result[3] = -C*g*self.epsilon*omega*sx*cy*zdep_add*tdep # hx
- result[4] = C*f*self.epsilon*omega*cx*sy*zdep_add*tdep
-
- return result
-
-
-
-
-class RectangularCavityMode(RectangularWaveguideMode):
- """A rectangular TM cavity mode."""
-
- def __init__(self, *args, **kwargs):
- if "scale" in kwargs:
- kwargs["forward_coeff"] = scale
- kwargs["backward_coeff"] = scale
- else:
- kwargs["forward_coeff"] = 1
- kwargs["backward_coeff"] = 1
- RectangularWaveguideMode.__init__(self, *args, **kwargs)
-
-
-
-
-
-# dipole solution -------------------------------------------------------------
-class DipoleFarField:
- """Dipole EM field.
-
- See U{http://piki.tiker.net/wiki/Dipole}.
- """
- def __init__(self, q, d, omega, epsilon, mu):
- self.q = q
- self.d = d
-
- self.epsilon = epsilon
- self.mu = mu
- self.c = c = 1/sqrt(mu*epsilon)
-
- self.omega = omega
-
- freq = omega/(2*pi)
- self.wavelength = c/freq
-
- def far_field_mask(self, x, el):
- if la.norm(x) > 0.5*self.wavelength:
- return 1
- else:
- return 0
-
- def set_time(self, t):
- self.t = t
-
- shape = (6,)
-
- def __call__(self, x, el):
- # coordinates ---------------------------------------------------------
- r, theta, phi = SphericalFieldAdapter.r_theta_phi_from_xyz(x)
-
- # copy instance variables for easier access ---------------------------
- q = self.q
- d = self.d
-
- epsilon = self.epsilon
- mu = self.mu
- c = self.c
-
- omega = self.omega
- t = self.t
-
- # field components ----------------------------------------------------
- tdep = omega*t-omega*r/c
- e_r = (2*cos(phi)*q*d) / (4*pi*epsilon) * (
- 1/r**3 * sin(tdep)
- +omega/(c*r**2) * cos(tdep))
- e_phi = (sin(phi)*q*d) / (4*pi*epsilon) * (
- (1/r**3 - omega**2/(c**2*r)*sin(tdep))
- + omega/(c*r**2)*cos(tdep))
- h_theta = (omega*sin(phi)*q*d)/(4*pi) * (
- - omega/(c*r)*sin(tdep)
- + 1/r**2*cos(tdep))
-
- return [e_r, 0, e_phi, 0, h_theta, 0]
-
- def source_modulation(self, t):
- j0 = self.q*self.d*self.omega/self.epsilon
- return j0*cos(self.omega*t)
-
-
-
-
-# analytic solution tools -----------------------------------------------------
-def check_time_harmonic_solution(discr, mode, c_sol):
- from grudge.discretization import bind_nabla, bind_mass_matrix
- from grudge.visualization import SiloVisualizer
- from grudge.silo import SiloFile
- from grudge.tools import dot, cross
- from grudge.silo import DB_VARTYPE_VECTOR
-
- def curl(field):
- return cross(nabla, field)
-
- vis = SiloVisualizer(discr)
-
- nabla = bind_nabla(discr)
- mass = bind_mass_matrix(discr)
-
- def rel_l2_error(err, base):
- def l2_norm(field):
- return sqrt(dot(field, mass*field))
-
- base_l2 = l2_norm(base)
- err_l2 = l2_norm(err)
- if base_l2 == 0:
- if err_l2 == 0:
- return 0.
- else:
- return float("inf")
- else:
- return err_l2/base_l2
-
- dt = 0.1
-
- for step in range(10):
- t = step*dt
- mode.set_time(t)
- fields = discr.interpolate_volume_function(c_sol)
-
- er = fields[0:3]
- hr = fields[3:6]
- ei = fields[6:9]
- hi = fields[9:12]
-
- silo = SiloFile("em-complex-%04d.silo" % step)
- vis.add_to_silo(silo,
- vectors=[
- ("curl_er", curl(er)),
- ("om_hi", -mode.mu*mode.omega*hi),
- ("curl_hr", curl(hr)),
- ("om_ei", mode.epsilon*mode.omega*hi),
- ],
- expressions=[
- ("diff_er", "curl_er-om_hi", DB_VARTYPE_VECTOR),
- ("diff_hr", "curl_hr-om_ei", DB_VARTYPE_VECTOR),
- ],
- write_coarse_mesh=True,
- time=t, step=step
- )
-
- er_res = curl(er) + mode.mu *mode.omega*hi
- ei_res = curl(ei) - mode.mu *mode.omega*hr
- hr_res = curl(hr) - mode.epsilon*mode.omega*ei
- hi_res = curl(hi) + mode.epsilon*mode.omega*er
-
- print("time=%f, rel l2 residual in Re[E]=%g\tIm[E]=%g\tRe[H]=%g\tIm[H]=%g" % (
- t,
- rel_l2_error(er_res, er),
- rel_l2_error(ei_res, ei),
- rel_l2_error(hr_res, hr),
- rel_l2_error(hi_res, hi),
- ))
-
diff --git a/examples/maxwell/cavities.py b/examples/maxwell/cavities.py
index 6fb7d54ac4eb69cf87705fce00268f68debfd4ba..85a7c3dec0cef99b4a72fe9c23439c4212c0c32d 100644
--- a/examples/maxwell/cavities.py
+++ b/examples/maxwell/cavities.py
@@ -1,260 +1,131 @@
-# Copyright (C) 2007 Andreas Kloeckner
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-from __future__ import division
-from __future__ import absolute_import
-from __future__ import print_function
-import numpy as np
-
-import logging
-logger = logging.getLogger(__name__)
-
-
-def main(write_output=True, allow_features=None, flux_type_arg=1,
- bdry_flux_type_arg=None, extra_discr_args={}):
- from grudge.mesh.generator import make_cylinder_mesh, make_box_mesh
- from grudge.tools import EOCRecorder, to_obj_array
- from math import sqrt, pi # noqa
- from analytic_solutions import ( # noqa
- RealPartAdapter,
- SplitComplexAdapter,
- CylindricalFieldAdapter,
- CylindricalCavityMode,
- RectangularWaveguideMode,
- RectangularCavityMode)
- from grudge.models.em import MaxwellOperator
-
- logging.basicConfig(level=logging.DEBUG)
-
- from grudge.backends import guess_run_context
- rcon = guess_run_context(allow_features)
-
- epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
- mu0 = 4*pi*1e-7 # N/A**2.
- epsilon = 1*epsilon0
- mu = 1*mu0
-
- eoc_rec = EOCRecorder()
-
- cylindrical = False
- periodic = False
-
- if cylindrical:
- R = 1
- d = 2
- mode = CylindricalCavityMode(m=1, n=1, p=1,
- radius=R, height=d,
- epsilon=epsilon, mu=mu)
- # r_sol = CylindricalFieldAdapter(RealPartAdapter(mode))
- # c_sol = SplitComplexAdapter(CylindricalFieldAdapter(mode))
-
- if rcon.is_head_rank:
- mesh = make_cylinder_mesh(radius=R, height=d, max_volume=0.01)
- else:
- if periodic:
- mode = RectangularWaveguideMode(epsilon, mu, (3, 2, 1))
- periodicity = (False, False, True)
- else:
- periodicity = None
- mode = RectangularCavityMode(epsilon, mu, (1, 2, 2))
-
- if rcon.is_head_rank:
- mesh = make_box_mesh(max_volume=0.001, periodicity=periodicity)
-
- if rcon.is_head_rank:
- mesh_data = rcon.distribute_mesh(mesh)
- else:
- mesh_data = rcon.receive_mesh()
-
- for order in [4, 5, 6]:
- #for order in [1,2,3,4,5,6]:
- extra_discr_args.setdefault("debug", []).extend([
- "cuda_no_plan", "cuda_dump_kernels"])
-
- op = MaxwellOperator(epsilon, mu,
- flux_type=flux_type_arg,
- bdry_flux_type=bdry_flux_type_arg)
-
- discr = rcon.make_discretization(mesh_data, order=order,
- tune_for=op.sym_operator(),
- **extra_discr_args)
-
- from grudge.visualization import VtkVisualizer
- if write_output:
- vis = VtkVisualizer(discr, rcon, "em-%d" % order)
-
- mode.set_time(0)
-
- def get_true_field():
- return discr.convert_volume(
- to_obj_array(mode(discr)
- .real.astype(discr.default_scalar_type).copy()),
- kind=discr.compute_kind)
-
- fields = get_true_field()
+"""Minimal example of a grudge driver."""
- if rcon.is_head_rank:
- print("---------------------------------------------")
- print("order %d" % order)
- print("---------------------------------------------")
- print("#elements=", len(mesh.elements))
+from __future__ import division, print_function
- from grudge.timestep.runge_kutta import LSRK4TimeStepper
- stepper = LSRK4TimeStepper(dtype=discr.default_scalar_type, rcon=rcon)
- #from grudge.timestep.dumka3 import Dumka3TimeStepper
- #stepper = Dumka3TimeStepper(3, dtype=discr.default_scalar_type, rcon=rcon)
+__copyright__ = "Copyright (C) 2015 Andreas Kloeckner"
- # {{{ diagnostics setup
+__license__ = """
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
- from pytools.log import LogManager, add_general_quantities, \
- add_simulation_quantities, add_run_info
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
- if write_output:
- log_file_name = "maxwell-%d.dat" % order
- else:
- log_file_name = None
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
- logmgr = LogManager(log_file_name, "w", rcon.communicator)
- add_run_info(logmgr)
- add_general_quantities(logmgr)
- add_simulation_quantities(logmgr)
- discr.add_instrumentation(logmgr)
- stepper.add_instrumentation(logmgr)
-
- from pytools.log import IntervalTimer
- vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
- logmgr.add_quantity(vis_timer)
-
- from grudge.log import EMFieldGetter, add_em_quantities
- field_getter = EMFieldGetter(discr, op, lambda: fields)
- add_em_quantities(logmgr, op, field_getter)
-
- logmgr.add_watches(
- ["step.max", "t_sim.max",
- ("W_field", "W_el+W_mag"),
- "t_step.max"]
- )
-
- # }}}
-
- # {{{ timestep loop
-
- rhs = op.bind(discr)
- final_time = 0.5e-9
-
- try:
- from grudge.timestep import times_and_steps
- step_it = times_and_steps(
- final_time=final_time, logmgr=logmgr,
- max_dt_getter=lambda t: op.estimate_timestep(discr,
- stepper=stepper, t=t, fields=fields))
+import numpy as np
+import pyopencl as cl
+from grudge.shortcuts import set_up_rk4
+from grudge import sym, bind, Discretization
- for step, t, dt in step_it:
- if step % 50 == 0 and write_output:
- sub_timer = vis_timer.start_sub_timer()
- e, h = op.split_eh(fields)
- visf = vis.make_file("em-%d-%04d" % (order, step))
- vis.add_data(
- visf,
- [
- ("e",
- discr.convert_volume(e, kind="numpy")),
- ("h",
- discr.convert_volume(h, kind="numpy")),
- ],
- time=t, step=step)
- visf.close()
- sub_timer.stop().submit()
+from grudge.models.em import get_rectangular_cavity_mode
- fields = stepper(fields, t, dt, rhs)
- mode.set_time(final_time)
+def main(dims, write_output=True, order=4):
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
- eoc_rec.add_data_point(order,
- discr.norm(fields-get_true_field()))
+ from meshmode.mesh.generation import generate_regular_rect_mesh
+ mesh = generate_regular_rect_mesh(
+ a=(0.0,)*dims,
+ b=(1.0,)*dims,
+ n=(5,)*dims)
- finally:
- if write_output:
- vis.close()
+ discr = Discretization(cl_ctx, mesh, order=order)
- logmgr.close()
- discr.close()
+ if 0:
+ epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
+ mu0 = 4*np.pi*1e-7 # N/A**2.
+ epsilon = 1*epsilon0
+ mu = 1*mu0
+ else:
+ epsilon = 1
+ mu = 1
- if rcon.is_head_rank:
- print()
- print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
+ from grudge.models.em import MaxwellOperator
+ op = MaxwellOperator(epsilon, mu, flux_type=0.5, dimensions=dims)
- # }}}
+ queue = cl.CommandQueue(discr.cl_context)
- assert eoc_rec.estimate_order_of_convergence()[0, 1] > 6
+ if dims == 3:
+ sym_mode = get_rectangular_cavity_mode(1, (1, 2, 2))
+ fields = bind(discr, sym_mode)(queue, t=0, epsilon=epsilon, mu=mu)
+ else:
+ sym_mode = get_rectangular_cavity_mode(1, (2, 3))
+ fields = bind(discr, sym_mode)(queue, t=0)
+ # FIXME
+ #dt = op.estimate_rk4_timestep(discr, fields=fields)
-# {{{ entry points for py.test
+ op.check_bc_coverage(mesh)
-from pytools.test import mark_test
+ # print(sym.pretty(op.sym_operator()))
+ bound_op = bind(discr, op.sym_operator())
+ def rhs(t, w):
+ return bound_op(queue, t=t, w=w)
-@mark_test.long
-def test_maxwell_cavities():
- main(write_output=False)
+ if mesh.dim == 2:
+ dt = 0.004
+ elif mesh.dim == 3:
+ dt = 0.002
+ dt_stepper = set_up_rk4("w", dt, fields, rhs)
-@mark_test.long
-def test_maxwell_cavities_lf():
- main(write_output=False, flux_type_arg="lf", bdry_flux_type_arg=1)
+ final_t = dt * 600
+ nsteps = int(final_t/dt)
+ print("dt=%g nsteps=%d" % (dt, nsteps))
-@mark_test.mpi
-@mark_test.long
-def test_maxwell_cavities_mpi():
- from pytools.mpi import run_with_mpi_ranks
- run_with_mpi_ranks(__file__, 2, main,
- write_output=False, allow_features=["mpi"])
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+ step = 0
-def test_cuda():
- try:
- from pycuda.tools import mark_cuda_test
- except ImportError:
- pass
+ norm = bind(discr, sym.norm(2, sym.var("u")))
- yield "SP CUDA Maxwell", mark_cuda_test(
- do_test_maxwell_cavities_cuda), np.float32
- yield "DP CUDA Maxwell", mark_cuda_test(
- do_test_maxwell_cavities_cuda), np.float64
+ from time import time
+ t_last_step = time()
+ e, h = op.split_eh(fields)
-def do_test_maxwell_cavities_cuda(dtype):
- import pytest # noqa
+ if 1:
+ vis.write_vtk_file("fld-%04d.vtu" % step,
+ [
+ ("e", e),
+ ("h", h),
+ ])
- main(write_output=False, allow_features=["cuda"],
- extra_discr_args=dict(
- debug=["cuda_no_plan"],
- default_scalar_type=dtype,
- ))
+ for event in dt_stepper.run(t_end=final_t):
+ if isinstance(event, dt_stepper.StateComputed):
+ assert event.component_id == "w"
-# }}}
+ step += 1
+ print(step, event.t, norm(queue, u=e[0]), norm(queue, u=e[1]),
+ norm(queue, u=h[0]), norm(queue, u=h[1]),
+ time()-t_last_step)
+ if step % 10 == 0:
+ e, h = op.split_eh(event.state_component)
+ vis.write_vtk_file("fld-%04d.vtu" % step,
+ [
+ ("e", e),
+ ("h", h),
+ ])
+ t_last_step = time()
-# entry point -----------------------------------------------------------------
if __name__ == "__main__":
- from pytools.mpi import in_mpi_relaunch
- if in_mpi_relaunch():
- test_maxwell_cavities_mpi()
- else:
- do_test_maxwell_cavities_cuda(np.float32)
+ main(3)
diff --git a/grudge/discretization.py b/grudge/discretization.py
index cf640c2c4a4338463e41625530fbbf98d7dddb71..a1ca6303ce40f787ddce458438c88e983b8b94d3 100644
--- a/grudge/discretization.py
+++ b/grudge/discretization.py
@@ -25,6 +25,7 @@ THE SOFTWARE.
from pytools import memoize_method
from grudge import sym
+import numpy as np
# FIXME Naming not ideal
@@ -292,6 +293,14 @@ class DGDiscretizationWithBoundaries(DiscretizationBase):
def ambient_dim(self):
return self._volume_discr.ambient_dim
+ @property
+ def real_dtype(self):
+ return self._volume_discr.real_dtype
+
+ @property
+ def complex_dtype(self):
+ return self._volume_discr.complex_dtype
+
@property
def mesh(self):
return self._volume_discr.mesh
@@ -318,6 +327,11 @@ class PointsDiscretization(DiscretizationBase):
def __init__(self, nodes):
self._nodes = nodes
+ self.real_dtype = np.dtype(np.float64)
+ self.complex_dtype = np.dtype({
+ np.float32: np.complex64,
+ np.float64: np.complex128
+ }[self.real_dtype.type])
def ambient_dim(self):
return self._nodes.shape[0]
diff --git a/grudge/execution.py b/grudge/execution.py
index 99a3d4e5aa3bb971b83489583247e9625f3638f1..e4aebe5ca18b495acf4e608ff9f4cdfd218f57b1 100644
--- a/grudge/execution.py
+++ b/grudge/execution.py
@@ -95,14 +95,40 @@ class ExecutionMapper(mappers.Evaluator,
# FIXME: Make a way to register functions
- pars = [self.rec(p) for p in expr.parameters]
- if any(isinstance(par, cl.array.Array) for par in pars):
- import pyopencl.clmath as clmath
- func = getattr(clmath, expr.function.name)
- else:
+ args = [self.rec(p) for p in expr.parameters]
+ from numbers import Number
+ representative_arg = args[0]
+ if (
+ isinstance(representative_arg, Number)
+ or (isinstance(representative_arg, np.ndarray)
+ and representative_arg.shape == ())):
func = getattr(np, expr.function.name)
+ return func(*args)
+
+ cached_name = "map_call_knl_"
+
+ i = Variable("i")
+ func = Variable(expr.function.name)
+ if expr.function.name == "fabs": # FIXME
+ func = Variable("abs")
+ cached_name += "abs"
+ else:
+ cached_name += expr.function.name
+
+ @memoize_in(self.bound_op, cached_name)
+ def knl():
+ knl = lp.make_kernel(
+ "{[i]: 0<=i>> list(partial_to_all_subset_indices([[False, True, True], [True,False,True]]))
+ [array([0 1]), array([2 3]
+ """
+
+ idx = base
+ for subset in subsets:
+ result = []
+ for is_in in subset:
+ if is_in:
+ result.append(idx)
+ idx += 1
+
+ yield np.array(result, dtype=np.intp)
diff --git a/test/test_grudge.py b/test/test_grudge.py
index 6602699039c357fadac3fc78cc7262285ce596f2..84c46168ede26efe1a42935501d69fba99b7700a 100644
--- a/test/test_grudge.py
+++ b/test/test_grudge.py
@@ -333,6 +333,76 @@ def test_convergence_advec(ctx_factory, mesh_name, mesh_pars, op_type, flux_type
assert eoc_rec.order_estimate() > order
+@pytest.mark.parametrize("order", [3, 4, 5])
+# test: 'test_convergence_advec(cl._csc, "disk", [0.1, 0.05], "strong", "upwind", 3)'
+def test_convergence_maxwell(ctx_factory, order, visualize=False):
+ """Test whether 3D maxwells actually converges"""
+
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
+
+ from pytools.convergence import EOCRecorder
+ eoc_rec = EOCRecorder()
+
+ dims = 3
+ ns = [4, 6, 8]
+ for n in ns:
+ from meshmode.mesh.generation import generate_regular_rect_mesh
+ mesh = generate_regular_rect_mesh(
+ a=(0.0,)*dims,
+ b=(1.0,)*dims,
+ n=(n,)*dims)
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
+
+ epsilon = 1
+ mu = 1
+
+ from grudge.models.em import get_rectangular_cavity_mode
+ sym_mode = get_rectangular_cavity_mode(1, (1, 2, 2))
+
+ analytic_sol = bind(discr, sym_mode)
+ fields = analytic_sol(queue, t=0, epsilon=epsilon, mu=mu)
+
+ from grudge.models.em import MaxwellOperator
+ op = MaxwellOperator(epsilon, mu, flux_type=0.5, dimensions=dims)
+ op.check_bc_coverage(mesh)
+ bound_op = bind(discr, op.sym_operator())
+
+ def rhs(t, w):
+ return bound_op(queue, t=t, w=w)
+
+ dt = 0.002
+ final_t = dt * 5
+ nsteps = int(final_t/dt)
+
+ from grudge.shortcuts import set_up_rk4
+ dt_stepper = set_up_rk4("w", dt, fields, rhs)
+
+ print("dt=%g nsteps=%d" % (dt, nsteps))
+
+ norm = bind(discr, sym.norm(2, sym.var("u")))
+
+ step = 0
+ for event in dt_stepper.run(t_end=final_t):
+ if isinstance(event, dt_stepper.StateComputed):
+ assert event.component_id == "w"
+ esc = event.state_component
+
+ step += 1
+ print(step)
+
+ sol = analytic_sol(queue, mu=mu, epsilon=epsilon, t=step * dt)
+ vals = [norm(queue, u=(esc[i] - sol[i])) / norm(queue, u=sol[i]) for i in range(5)] # noqa E501
+ total_error = sum(vals)
+ eoc_rec.add_data_point(1.0/n, total_error)
+
+ print(eoc_rec.pretty_print(abscissa_label="h",
+ error_label="L2 Error"))
+
+ assert eoc_rec.order_estimate() > order
+
+
def test_foreign_points(ctx_factory):
pytest.importorskip("sumpy")
import sumpy.point_calculus as pc