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