diff --git a/examples/advection/gas_dynamics_initials.py b/examples/advection/gas_dynamics_initials.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f4e4f4164c4484cd0b1ded50fb798a750cda72e
--- /dev/null
+++ b/examples/advection/gas_dynamics_initials.py
@@ -0,0 +1,172 @@
+# Copyright (C) 2008 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
+import numpy
+import numpy.linalg as la
+from six.moves import range
+from grudge import sym
+
+
+
+
+class UniformMachFlow:
+ def __init__(self, mach=0.1, p=1, rho=1, reynolds=100,
+ gamma=1.4, prandtl=0.72, char_length=1, spec_gas_const=287.1,
+ angle_of_attack=None, direction=None, gaussian_pulse_at=None,
+ pulse_magnitude=0.1):
+ """
+ :param direction: is a vector indicating the direction of the
+ flow. Only one of angle_of_attack and direction may be
+ specified. Only the direction, not the magnitude, of
+ direction is taken into account.
+
+ :param angle_of_attack: if not None, specifies the angle of
+ the flow along the Y axis, where the flow is
+ directed along the X axis.
+ """
+ if angle_of_attack is not None and direction is not None:
+ raise ValueError("Only one of angle_of_attack and "
+ "direction may be specified.")
+
+ if angle_of_attack is None and direction is None:
+ angle_of_attack = 0
+
+ if direction is not None:
+ self.direction = direction/la.norm(direction)
+ else:
+ self.direction = None
+
+ self.mach = mach
+ self.p = p
+ self.rho = rho
+
+ self.gamma = gamma
+ self.prandtl = prandtl
+ self.reynolds = reynolds
+ self.length = char_length
+ self.spec_gas_const = spec_gas_const
+
+ self.angle_of_attack = angle_of_attack
+
+ self.gaussian_pulse_at = gaussian_pulse_at
+ self.pulse_magnitude = pulse_magnitude
+
+ self.c = (self.gamma * p / rho)**0.5
+ u = self.velocity = mach * self.c
+ self.e = p / (self.gamma - 1) + rho / 2 * u**2
+
+ if numpy.isinf(self.reynolds):
+ self.mu = 0
+ else:
+ self.mu = u * self.length * rho / self.reynolds
+
+ def direction_vector(self, dimensions):
+ # this must be done here because dimensions is not known above
+ if self.direction is None:
+ assert self.angle_of_attack is not None
+ direction = numpy.zeros(dimensions, dtype=numpy.float64)
+ direction[0] = numpy.cos(
+ self.angle_of_attack / 180. * numpy.pi)
+ direction[1] = numpy.sin(
+ self.angle_of_attack / 180. * numpy.pi)
+ return direction
+ else:
+ return self.direction
+
+ def __call__(self, t, x_vec):
+ ones = numpy.ones_like(x_vec[0])
+ rho_field = ones*self.rho
+
+ if self.gaussian_pulse_at is not None:
+ rel_to_pulse = [x_vec[i] - self.gaussian_pulse_at[i]
+ for i in range(len(x_vec))]
+ rho_field += self.pulse_magnitude * self.rho * numpy.exp(
+ - sum(rtp_i**2 for rtp_i in rel_to_pulse)/2)
+
+ direction = self.direction_vector(x_vec.shape[0])
+
+ from grudge.tools import make_obj_array
+ u_field = make_obj_array([ones*self.velocity*dir_i
+ for dir_i in direction])
+
+ from grudge.tools import join_fields
+ return join_fields(rho_field, self.e*ones, self.rho*u_field)
+
+ def volume_interpolant(self, t, discr):
+ return discr.convert_volume(
+ self(t, discr.nodes.T),
+ kind=discr.compute_kind,
+ dtype=discr.default_scalar_type)
+
+ def boundary_interpolant(self, t, discr, tag):
+ return discr.convert_boundary(
+ self(t, discr.get_boundary(tag).nodes.T),
+ tag=tag, kind=discr.compute_kind,
+ dtype=discr.default_scalar_type)
+
+
+
+
+
+
+
+class Vortex:
+ def __init__(self):
+ self.beta = 5
+ self.gamma = 1.4
+ self.center = numpy.array([5, 0])
+ self.velocity = numpy.array([0.3, 0.3])
+ self.final_time = 0.5
+
+ self.mu = 0
+ self.prandtl = 0.72
+ self.spec_gas_const = 287.1
+
+ def __call__(self):
+ x_vec = sym.nodes(2)
+ t = sym.var("t", sym.DD_SCALAR)
+ vortex_loc = self.center + t*self.velocity
+
+ # coordinates relative to vortex center
+ x_rel = x_vec[0] - vortex_loc[0]
+ y_rel = x_vec[1] - vortex_loc[1]
+
+ # Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
+ # also JSH/TW Nodal DG Methods, p. 209
+
+ from math import pi
+ r = sym.sqrt(x_rel**2+y_rel**2)
+ expterm = self.beta*sym.exp(1-r**2)
+ u = self.velocity[0] - expterm*y_rel/(2*pi)
+ v = self.velocity[1] + expterm*x_rel/(2*pi)
+ rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
+ p = rho**self.gamma
+
+ e = p/(self.gamma-1) + rho/2*(u**2+v**2)
+
+ from pytools.obj_array import join_fields
+ return join_fields(rho, e, rho*u, rho*v)
+
+ def volume_interpolant(self):
+ return self()
+
+
+
+
diff --git a/examples/advection/new.py b/examples/advection/new.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a8566e064120009c3dff0028e7114e3b7219d5c
--- /dev/null
+++ b/examples/advection/new.py
@@ -0,0 +1,138 @@
+from __future__ import division, absolute_import
+
+__copyright__ = "Copyright (C) 2017 Bogdan Enache"
+
+__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:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+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.
+"""
+
+
+import numpy as np
+import pyopencl as cl # noqa
+import pyopencl.array # noqa
+import pyopencl.clmath # noqa
+
+import pytest # noqa
+
+from pyopencl.tools import ( # noqa
+ pytest_generate_tests_for_pyopencl as pytest_generate_tests)
+
+import logging
+logger = logging.getLogger(__name__)
+
+from grudge import sym, bind, DGDiscretizationWithBoundaries
+from pytools.obj_array import join_fields
+
+
+def main(write_output=True, order=4):
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
+
+ dim = 2
+
+ resolution = 15
+ from meshmode.mesh.generation import generate_regular_rect_mesh
+ mesh = generate_regular_rect_mesh(a=(0, -5), b=(10, 5),
+ n=(resolution, resolution), order=order)
+
+ h = 1/resolution
+
+
+ source_center = np.array([3, -2])
+ source_center2 = np.array([6, 2])
+ source_width = 0.5
+
+ sym_x = sym.nodes(2)
+ sym_source_center_dist = sym_x - source_center
+ sym_source_center_dist2 = sym_x - source_center2
+
+
+ def f_gaussian(x):
+ return join_fields(sym.exp(
+ -np.dot(sym_source_center_dist, sym_source_center_dist)
+ / source_width**2) ,sym.exp(
+ -np.dot(sym_source_center_dist2, sym_source_center_dist2)
+ / source_width**2) )
+
+ from gas_dynamics_initials import Vortex
+ flow = Vortex()
+ bund = sym.interp("vol", sym.BTAG_ALL)(flow.volume_interpolant()[0:2])
+
+
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
+
+ def sym_operator():
+ u = sym.make_sym_array("u", 2)
+ def mflux(pair):
+ normal = sym.normal(pair.dd, dim)
+ return pair.avg*np.dot(normal, [1,1])
+
+ def flux(pair):
+ return sym.interp(pair.dd, "all_faces")(
+ mflux(pair))
+ stiff_t = sym.stiffness_t(dim)
+ return sym.InverseMassOperator()(
+ np.dot(stiff_t, [u,u])
+ - sym.FaceMassOperator()(
+ flux(sym.int_tpair(u))
+ + flux(sym.bv_tpair(sym.BTAG_ALL, u, bund))
+
+ )
+ )
+
+
+ bound_op = bind(discr, sym_operator())
+
+ #u = bind(discr, f_gaussian(sym.nodes(dim)))(queue, t=0)
+ u = bind(discr, flow.volume_interpolant()[0:2])(queue, t=0)
+
+ def rhs(t, u):
+ return bound_op(queue, t=t, u=u)
+
+ final_time = 1
+ dt = 0.001
+ print(dt)
+
+ from grudge.shortcuts import set_up_rk4
+ dt_stepper = set_up_rk4("u", dt, u, rhs)
+
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=2*order)
+
+ step = 0
+ vis.write_vtk_file("fld-%04d.vtu" % step,
+ #[("u", event.state_component[0])])
+ [("u", u[0]), ("u2", u[1])]
+ )
+
+ for event in dt_stepper.run(t_end=final_time):
+ if isinstance(event, dt_stepper.StateComputed):
+
+ step += 1
+ print(step)
+
+ vis.write_vtk_file("fld-%04d.vtu" % step,
+ #[("u", event.state_component[0])])
+ [("u", event.state_component[0]), ("u2", event.state_component[1])]
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/advection/var-velocity.py b/examples/advection/var-velocity.py
index 00e4f0c8496dced20a21e808e68986ecec1d5be2..7cf7248966a1d8c61ab7d9d73c8263ca52768b54 100644
--- a/examples/advection/var-velocity.py
+++ b/examples/advection/var-velocity.py
@@ -56,9 +56,9 @@ def main(write_output=True, order=4):
sym_x = sym.nodes(2)
- advec_v = join_fields(-1*sym_x[1], sym_x[0]) / 2
+ advec_v = sym_x/sym_x
- flux_type = "upwind"
+ flux_type = "central"
source_center = np.array([0.1, 0.1])
source_width = 0.05
@@ -109,6 +109,8 @@ def main(write_output=True, order=4):
dt = dt_factor * h/order**2
nsteps = (final_time // dt) + 1
dt = final_time/nsteps + 1e-15
+ dt = 0.001
+ print(dt)
from grudge.shortcuts import set_up_rk4
dt_stepper = set_up_rk4("u", dt, u, rhs)
@@ -122,7 +124,7 @@ def main(write_output=True, order=4):
if isinstance(event, dt_stepper.StateComputed):
step += 1
- if step % 30 == 0:
+ if step % 2 == 0:
print(step)
vis.write_vtk_file("fld-%04d.vtu" % step,
diff --git a/examples/gas_dynamics/euler/sine-wave.py b/examples/gas_dynamics/euler/sine-wave.py
index 451befdbe9613f733ace9324a8770ce8de0d295f..13aeb5870086280fb10877248abec3ee70f65a77 100644
--- a/examples/gas_dynamics/euler/sine-wave.py
+++ b/examples/gas_dynamics/euler/sine-wave.py
@@ -13,17 +13,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import pyopencl as cl # noqa
+import numpy as np
-
-
-from __future__ import division
-from __future__ import absolute_import
-from __future__ import print_function
-import numpy
-import numpy.linalg as la
-
-
-
+from grudge import sym, bind, DGDiscretizationWithBoundaries
class SineWave:
def __init__(self):
@@ -32,161 +25,115 @@ class SineWave:
self.prandtl = 0.72
self.spec_gas_const = 287.1
- def __call__(self, t, x_vec):
- rho = 2 + numpy.sin(x_vec[0] + x_vec[1] + x_vec[2] - 2 * t)
- velocity = numpy.array([1, 1, 0])
+ def sym_operator(self):
+ t = sym.var("t", sym.DD_SCALAR)
+ sym_nds = sym.nodes(3)
+ rho = 2 + sym.sin(sym_nds[0] + sym_nds[1] + sym_nds[2] - 2 * t)
+ velocity = np.array([1, 1, 0])
p = 1
- e = p/(self.gamma-1) + rho/2 * numpy.dot(velocity, velocity)
+ e = p/(self.gamma-1) + rho/2 * np.dot(velocity, velocity)
rho_u = rho * velocity[0]
rho_v = rho * velocity[1]
rho_w = rho * velocity[2]
- from grudge.tools import join_fields
+ from pytools.obj_array import join_fields
return join_fields(rho, e, rho_u, rho_v, rho_w)
def properties(self):
return(self.gamma, self.mu, self.prandtl, self.spec_gas_const)
- def volume_interpolant(self, t, discr):
- return discr.convert_volume(
- self(t, discr.nodes.T),
- kind=discr.compute_kind)
-
- def boundary_interpolant(self, t, discr, tag):
- return discr.convert_boundary(
- self(t, discr.get_boundary(tag).nodes.T),
- tag=tag, kind=discr.compute_kind)
-
-
-
+def main(order = 3, visualize=False):
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
-def main(final_time=1, write_output=False):
- from grudge.backends import guess_run_context
- rcon = guess_run_context()
- from grudge.tools import EOCRecorder, to_obj_array
+ from pytools.convergence import EOCRecorder
eoc_rec = EOCRecorder()
- if rcon.is_head_rank:
- from grudge.mesh import make_box_mesh
- mesh = make_box_mesh((0,0,0), (10,10,10), max_volume=0.5)
- mesh_data = rcon.distribute_mesh(mesh)
- else:
- mesh_data = rcon.receive_mesh()
-
- for order in [3, 4, 5]:
- discr = rcon.make_discretization(mesh_data, order=order,
- default_scalar_type=numpy.float64)
-
- from grudge.visualization import SiloVisualizer, VtkVisualizer
- vis = VtkVisualizer(discr, rcon, "sinewave-%d" % order)
- #vis = SiloVisualizer(discr, rcon)
-
- sinewave = SineWave()
- fields = sinewave.volume_interpolant(0, discr)
- gamma, mu, prandtl, spec_gas_const = sinewave.properties()
-
- from grudge.mesh import BTAG_ALL
- from grudge.models.gas_dynamics import GasDynamicsOperator
- op = GasDynamicsOperator(dimensions=mesh.dimensions, gamma=gamma, mu=mu,
- prandtl=prandtl, spec_gas_const=spec_gas_const,
- bc_inflow=sinewave, bc_outflow=sinewave, bc_noslip=sinewave,
- inflow_tag=BTAG_ALL, source=None)
-
- euler_ex = op.bind(discr)
-
- max_eigval = [0]
- def rhs(t, q):
- ode_rhs, speed = euler_ex(t, q)
- max_eigval[0] = speed
- return ode_rhs
- rhs(0, fields)
-
- if rcon.is_head_rank:
- print("---------------------------------------------")
- print("order %d" % order)
- print("---------------------------------------------")
- print("#elements=", len(mesh.elements))
-
- from grudge.timestep import RK4TimeStepper
- stepper = RK4TimeStepper()
-
- # diagnostics setup ---------------------------------------------------
- from pytools.log import LogManager, add_general_quantities, \
- add_simulation_quantities, add_run_info
-
- if write_output:
- log_name = ("euler-sinewave-%(order)d-%(els)d.dat"
- % {"order":order, "els":len(mesh.elements)})
- else:
- log_name = False
- logmgr = LogManager(log_name, "w", rcon.communicator)
- add_run_info(logmgr)
- add_general_quantities(logmgr)
- add_simulation_quantities(logmgr)
- discr.add_instrumentation(logmgr)
- stepper.add_instrumentation(logmgr)
-
- logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
-
- # timestep loop -------------------------------------------------------
- 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, max_eigenvalue=max_eigval[0]))
-
- for step, t, dt in step_it:
- #if step % 10 == 0:
- if write_output:
- visf = vis.make_file("sinewave-%d-%04d" % (order, step))
-
- #from pyvisfile.silo import DB_VARTYPE_VECTOR
- vis.add_data(visf,
- [
- ("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
- ("e", discr.convert_volume(op.e(fields), kind="numpy")),
- ("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
- ("u", discr.convert_volume(op.u(fields), kind="numpy")),
-
- #("true_rho", op.rho(true_fields)),
- #("true_e", op.e(true_fields)),
- #("true_rho_u", op.rho_u(true_fields)),
- #("true_u", op.u(true_fields)),
-
- #("rhs_rho", op.rho(rhs_fields)),
- #("rhs_e", op.e(rhs_fields)),
- #("rhs_rho_u", op.rho_u(rhs_fields)),
- ],
- #expressions=[
- #("diff_rho", "rho-true_rho"),
- #("diff_e", "e-true_e"),
- #("diff_rho_u", "rho_u-true_rho_u", DB_VARTYPE_VECTOR),
-
- #("p", "0.4*(e- 0.5*(rho_u*u))"),
- #],
- time=t, step=step
- )
- visf.close()
-
- fields = stepper(fields, t, dt, rhs)
-
- finally:
- vis.close()
- logmgr.close()
- discr.close()
-
- true_fields = sinewave.volume_interpolant(t, discr)
- eoc_rec.add_data_point(order, discr.norm(fields-true_fields))
- print()
- print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
+ n = 3
+ from meshmode.mesh.generation import generate_regular_rect_mesh
+ mesh = generate_regular_rect_mesh(a=(-0.5, -0.5, -0.5), b=(0.5, 0.5, 0.5),
+ n=(n, n, n), order=order)
+
+ from meshmode.discretization.poly_element import QuadratureSimplexGroupFactory # noqa
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order,
+ quad_tag_to_group_factory={
+ #"product": None,
+ "product": QuadratureSimplexGroupFactory(order=3*order)
+ })
+
+
+ sinewave = SineWave()
+ bound_sine = bind(discr, sinewave.sym_operator())
+ fields = bound_sine(queue, t=0)
+ gamma, mu, prandtl, spec_gas_const = sinewave.properties()
+
+ from meshmode.mesh import BTAG_ALL
+ from grudge.models.gas_dynamics import GasDynamicsOperator
+ op = GasDynamicsOperator(dimensions=3, gamma=gamma, mu=mu,
+ prandtl=prandtl, spec_gas_const=spec_gas_const,
+ bc_inflow=sinewave, bc_outflow=sinewave, bc_noslip=sinewave,
+ inflow_tag=BTAG_ALL, source=None)
+ print(sym.pretty(op.sym_operator()))
+
+ euler_ex = bind(discr, op.sym_operator())
+ print(euler_ex)
+
+ max_eigval = [0]
+ def rhs(t, q):
+ #ode_rhs, speed = euler_ex(queue, t=t, q=q)
+ things = euler_ex(queue, t=t, q=q)
+ max_eigval[0] = things[-1]
+ return things[0:-1]
+ rhs(0, fields)
+
+ from grudge.shortcuts import set_up_rk4
+ dt = 0.000001
+ final_t = 0.0001
+ dt_stepper = set_up_rk4("q", dt, fields, rhs)
+
+ if visualize:
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+
+ step = 0
+ for event in dt_stepper.run(t_end=final_t):
+ if isinstance(event, dt_stepper.StateComputed):
+ #assert event.component_id == "u"
+ print(event.component_id)
+ fields = event.state_component
+
+ if visualize:
+ vis.write_vtk_file("fld-sinewave-%d-%04d.vtu" % (order, step),
+ [
+ ("rho", op.rho(fields)),
+ ("e", op.e(fields)),
+ ("rho_u", op.rho_u(fields)),
+ ("u", op.u(fields)),
+
+ #("true_rho", op.rho(true_fields)),
+ #("true_e", op.e(true_fields)),
+ #("true_rho_u", op.rho_u(true_fields)),
+ #("true_u", op.u(true_fields)),
+
+ #("rhs_rho", op.rho(rhs_fields)),
+ #("rhs_e", op.e(rhs_fields)),
+ #("rhs_rho_u", op.rho_u(rhs_fields)),
+ ])
+
+ step += 1
+ print(step)
+
+ true_fields = bound_sine(queue, t=t)
+ eoc_rec.add_data_point(order, discr.norm(fields-true_fields))
+ print()
+ print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
if __name__ == "__main__":
- main()
+ main(order=2,visualize=True)
diff --git a/examples/gas_dynamics/euler/test.py b/examples/gas_dynamics/euler/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..44d478f12a87481a6a6d5790f47c5a9a620c6889
--- /dev/null
+++ b/examples/gas_dynamics/euler/test.py
@@ -0,0 +1,106 @@
+import numpy as np
+import pyopencl as cl # noqa
+
+from grudge import sym, bind, DGDiscretizationWithBoundaries
+
+import numpy.linalg as la
+
+
+def test_convergence_maxwell(order, ns, 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 = 2
+ for n in ns:
+ h = 1.0/n
+ dt_factor = 4
+
+ #from meshmode.mesh.generation import generate_regular_rect_mesh
+ #mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5),
+ #n=(n, n), order=order)
+
+ from meshmode.mesh.generation import generate_warped_rect_mesh
+ mesh = generate_warped_rect_mesh(dims, order, n)
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
+
+ advec_v = np.array([0.1, 0.1])
+ norm_v = la.norm(advec_v)
+
+ def f(x):
+ return sym.sin(3*x)
+
+ def u_analytic(x):
+ return f(-np.dot(advec_v, x)/norm_v+sym.var("t", sym.DD_SCALAR)*norm_v)
+
+ from grudge.models.advection import WeakAdvectionOperator
+ analytic_sol = bind(discr, u_analytic(sym.nodes(dims)))
+
+ jac_tag = sym.area_element(2, 2, dd=sym.DOFDesc("vol"))
+ analytic_init = bind(discr, u_analytic(sym.nodes(dims)) / sym.sqrt(jac_tag))
+ J_mul = bind(discr, sym.var("u", sym.DOFDesc("vol")) * sym.sqrt(jac_tag))
+
+ fields = analytic_init(queue, t=0)
+
+ jac_bound = sym.interp(sym.DOFDesc("vol"), sym.BTAG_ALL)(jac_tag)
+ op = WeakAdvectionOperator(advec_v,
+ inflow_u=u_analytic(sym.nodes(dims, sym.BTAG_ALL) / sym.sqrt(jac_bound)),
+ flux_type="central")
+
+ bound_op = bind(discr, op.sym_operator())
+
+ def rhs(t, u):
+ return bound_op(queue, t=t, u=u)
+
+ final_t = 0.1
+ if visualize:
+ final_t = final_t * 10
+
+ dt = dt_factor * h/order**2
+ nsteps = (final_t // dt) + 1
+ dt = final_t/nsteps + 1e-15
+
+ from grudge.shortcuts import set_up_rk4
+ dt_stepper = set_up_rk4("u", 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 == "u"
+ esc = event.state_component
+
+ step += 1
+ print(step)
+ esc = J_mul(queue, u=esc)
+
+
+ sol = analytic_sol(queue, t=step * dt)
+ total_error = norm(queue, u=(esc - sol)) / norm(queue, u=sol)
+ eoc_rec.add_data_point(1.0/n, total_error)
+
+ if visualize:
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+ vis.write_vtk_file("fld-thing.vtu", [ ("a", esc), ("s", sol), ("e", (esc-sol)) ])
+ return
+
+
+
+ print(eoc_rec.pretty_print(abscissa_label="h",
+ error_label="L2 Error"))
+
+ #assert eoc_rec.order_estimate() > order
+
+
+if __name__ == "__main__":
+ test_convergence_maxwell(3, [15,20], True)
+
diff --git a/examples/gas_dynamics/euler/vortex.py b/examples/gas_dynamics/euler/vortex.py
index 9f9703f4d77f4c13dc07db0a52eb1ee36fd0ea5d..dd11a51639e76fd92f29931a62ed67a321c36ee7 100644
--- a/examples/gas_dynamics/euler/vortex.py
+++ b/examples/gas_dynamics/euler/vortex.py
@@ -21,181 +21,141 @@ from __future__ import absolute_import
from __future__ import print_function
import numpy
+import pyopencl as cl # noqa
+from grudge import sym, bind, DGDiscretizationWithBoundaries
-
-def main(write_output=True):
+def main(write_output=True, visualize=False):
from pytools import add_python_path_relative_to_script
add_python_path_relative_to_script("..")
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
- from grudge.backends import guess_run_context
- rcon = guess_run_context()
- from grudge.tools import EOCRecorder
+ from pytools.convergence import EOCRecorder
eoc_rec = EOCRecorder()
- if rcon.is_head_rank:
- from grudge.mesh.generator import \
- make_rect_mesh, \
- make_centered_regular_rect_mesh
+ from meshmode.mesh.generation import generate_regular_rect_mesh
- refine = 4
- mesh = make_centered_regular_rect_mesh((0,-5), (10,5), n=(9,9),
- post_refine_factor=refine)
- mesh_data = rcon.distribute_mesh(mesh)
- else:
- mesh_data = rcon.receive_mesh()
for order in [3, 4, 5]:
+ refine = 4
+ mesh = generate_regular_rect_mesh(a=(0,-5), b=(10,5), n=(6,6), order=order)
from gas_dynamics_initials import Vortex
flow = Vortex()
from grudge.models.gas_dynamics import (
GasDynamicsOperator, PolytropeEOS, GammaLawEOS)
- from grudge.mesh import BTAG_ALL
+ from meshmode.mesh import BTAG_ALL
+ from meshmode.discretization.poly_element import QuadratureSimplexGroupFactory
# works equally well for GammaLawEOS
op = GasDynamicsOperator(dimensions=2, mu=flow.mu,
prandtl=flow.prandtl, spec_gas_const=flow.spec_gas_const,
equation_of_state=PolytropeEOS(flow.gamma),
- bc_inflow=flow, bc_outflow=flow, bc_noslip=flow,
+ bc_inflow=flow.volume_interpolant(), bc_outflow=flow.volume_interpolant(), bc_noslip=flow.volume_interpolant(),
inflow_tag=BTAG_ALL, source=None)
- discr = rcon.make_discretization(mesh_data, order=order,
- default_scalar_type=numpy.float64,
- quad_min_degrees={
- "gasdyn_vol": 3*order,
- "gasdyn_face": 3*order,
- },
- tune_for=op.sym_operator(),
- debug=["cuda_no_plan"])
+ discr = DGDiscretizationWithBoundaries(cl_ctx,mesh, order=order,
+ quad_tag_to_group_factory={
+ "product": QuadratureSimplexGroupFactory(order=3*order)
+ })
+
+ if visualize:
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
- from grudge.visualization import SiloVisualizer, VtkVisualizer
- vis = VtkVisualizer(discr, rcon, "vortex-%d" % order)
- #vis = SiloVisualizer(discr, rcon)
- fields = flow.volume_interpolant(0, discr)
+ bound_flow_vol = bind(discr, flow.volume_interpolant())
+ fields = bound_flow_vol(queue, t=1)
+ print(fields)
- euler_ex = op.bind(discr)
+ euler_ex = bind(discr, op.sym_operator())
max_eigval = [0]
def rhs(t, q):
- ode_rhs, speed = euler_ex(t, q)
+ #ode_rhs, speed = euler_ex(queue, t=t, q=q)
+ ting = euler_ex(queue, t=t, q=q)
+ ode_rhs = ting[:-1]
+ speed = ting[-1]
+ #pu.db
max_eigval[0] = speed
return ode_rhs
rhs(0, fields)
- if rcon.is_head_rank:
- print("---------------------------------------------")
- print("order %d" % order)
- print("---------------------------------------------")
- print("#elements=", len(mesh.elements))
-
# limiter ------------------------------------------------------------
- from grudge.models.gas_dynamics import SlopeLimiter1NEuler
- limiter = SlopeLimiter1NEuler(discr, flow.gamma, 2, op)
+ #from grudge.models.gas_dynamics import SlopeLimiter1NEuler
+ #limiter = SlopeLimiter1NEuler(discr, flow.gamma, 2, op)
- from grudge.timestep.runge_kutta import SSP3TimeStepper
+ #from grudge.timestep.runge_kutta import SSP3TimeStepper
#stepper = SSP3TimeStepper(limiter=limiter)
- stepper = SSP3TimeStepper(
- vector_primitive_factory=discr.get_vector_primitive_factory())
-
- #from grudge.timestep import RK4TimeStepper
- #stepper = RK4TimeStepper()
-
- # diagnostics setup ---------------------------------------------------
- from pytools.log import LogManager, add_general_quantities, \
- add_simulation_quantities, add_run_info
-
- if write_output:
- log_file_name = "euler-%d.dat" % order
- else:
- log_file_name = None
-
- 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)
-
- logmgr.add_watches(["step.max", "t_sim.max", "t_step.max"])
-
- # timestep loop -------------------------------------------------------
- try:
- final_time = flow.final_time
- 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, max_eigenvalue=max_eigval[0]))
-
- print("run until t=%g" % final_time)
- for step, t, dt in step_it:
- if step % 10 == 0 and write_output:
- #if False:
- visf = vis.make_file("vortex-%d-%04d" % (order, step))
-
- #true_fields = vortex.volume_interpolant(t, discr)
-
- from pyvisfile.silo import DB_VARTYPE_VECTOR
- vis.add_data(visf,
- [
- ("rho", discr.convert_volume(op.rho(fields), kind="numpy")),
- ("e", discr.convert_volume(op.e(fields), kind="numpy")),
- ("rho_u", discr.convert_volume(op.rho_u(fields), kind="numpy")),
- ("u", discr.convert_volume(op.u(fields), kind="numpy")),
-
- #("true_rho", discr.convert_volume(op.rho(true_fields), kind="numpy")),
- #("true_e", discr.convert_volume(op.e(true_fields), kind="numpy")),
- #("true_rho_u", discr.convert_volume(op.rho_u(true_fields), kind="numpy")),
- #("true_u", discr.convert_volume(op.u(true_fields), kind="numpy")),
-
- #("rhs_rho", discr.convert_volume(op.rho(rhs_fields), kind="numpy")),
- #("rhs_e", discr.convert_volume(op.e(rhs_fields), kind="numpy")),
- #("rhs_rho_u", discr.convert_volume(op.rho_u(rhs_fields), kind="numpy")),
- ],
- #expressions=[
- #("diff_rho", "rho-true_rho"),
- #("diff_e", "e-true_e"),
- #("diff_rho_u", "rho_u-true_rho_u", DB_VARTYPE_VECTOR),
-
- #("p", "0.4*(e- 0.5*(rho_u*u))"),
- #],
- time=t, step=step
- )
- visf.close()
-
- fields = stepper(fields, t, dt, rhs)
- #fields = limiter(fields)
-
- assert not numpy.isnan(numpy.sum(fields[0]))
-
- true_fields = flow.volume_interpolant(final_time, discr)
- l2_error = discr.norm(fields-true_fields)
- l2_error_rho = discr.norm(op.rho(fields)-op.rho(true_fields))
- l2_error_e = discr.norm(op.e(fields)-op.e(true_fields))
- l2_error_rhou = discr.norm(op.rho_u(fields)-op.rho_u(true_fields))
- l2_error_u = discr.norm(op.u(fields)-op.u(true_fields))
-
- eoc_rec.add_data_point(order, l2_error)
- print()
- print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
-
- logmgr.set_constant("l2_error", l2_error)
- logmgr.set_constant("l2_error_rho", l2_error_rho)
- logmgr.set_constant("l2_error_e", l2_error_e)
- logmgr.set_constant("l2_error_rhou", l2_error_rhou)
- logmgr.set_constant("l2_error_u", l2_error_u)
- logmgr.set_constant("refinement", refine)
-
- finally:
- if write_output:
- vis.close()
-
- logmgr.close()
- discr.close()
+ #stepper = SSP3TimeStepper(
+ #vector_primitive_factory=discr.get_vector_primitive_factory())
+ from grudge.shortcuts import set_up_rk4
+ dt = 0.001
+ final_t = 0.1
+ dt_stepper = set_up_rk4("q", dt, fields, rhs)
+
+ if visualize:
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+
+ norm = bind(discr, sym.norm(2, sym.var("q")))
+
+ vis.write_vtk_file("fld-sinewave-%d-%04d.vtu" % (order, 0),
+ [
+ ("rho", op.rho(fields)),
+ ("e", op.e(fields)),
+ ("rho_u", op.rho_u(fields)),
+ ("u", op.u(fields))
+ #("true_rho", op.rho(true_fields)),
+ #("true_e", op.e(true_fields)),
+ #("true_rho_u", op.rho_u(true_fields)),
+ #("true_u", op.u(true_fields)
+ #("rhs_rho", op.rho(rhs_fields)),
+ #("rhs_e", op.e(rhs_fields)),
+ #("rhs_rho_u", op.rho_u(rhs_fields)),
+ ])
+ step = 1
+ for event in dt_stepper.run(t_end=final_t):
+ if isinstance(event, dt_stepper.StateComputed):
+ assert event.component_id == "q"
+ fields = event.state_component
+
+ if visualize:
+ vis.write_vtk_file("fld-sinewave-%d-%04d.vtu" % (order, step),
+ [
+ ("rho", op.rho(fields)),
+ ("e", op.e(fields)),
+ ("rho_u", op.rho_u(fields)),
+ ("u", op.u(fields)),
+
+ #("true_rho", op.rho(true_fields)),
+ #("true_e", op.e(true_fields)),
+ #("true_rho_u", op.rho_u(true_fields)),
+ #("true_u", op.u(true_fields)),
+
+ #("rhs_rho", op.rho(rhs_fields)),
+ #("rhs_e", op.e(rhs_fields)),
+ #("rhs_rho_u", op.rho_u(rhs_fields)),
+ ])
+ step += 1
+ print(step)
+
+
+
+ #true_fields = bound_flow_vol(queue, t=final_t)
+ #l2_error = norm(queue, q = fields-true_fields)
+ #l2_error_rho = discr.norm(op.rho(fields)-op.rho(true_fields))
+ #l2_error_e = discr.norm(op.e(fields)-op.e(true_fields))
+ #l2_error_rhou = discr.norm(op.rho_u(fields)-op.rho_u(true_fields))
+ #l2_error_u = discr.norm(op.u(fields)-op.u(true_fields))
+
+ #eoc_rec.add_data_point(order, l2_error)
+ #print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
+
+
# after order loop
assert eoc_rec.estimate_order_of_convergence()[0,1] > 6
@@ -204,7 +164,7 @@ def main(write_output=True):
if __name__ == "__main__":
- main()
+ main(visualize=True)
diff --git a/examples/gas_dynamics/euler/vortex2.py b/examples/gas_dynamics/euler/vortex2.py
new file mode 100644
index 0000000000000000000000000000000000000000..81f19c296cea3ef93a2ec50ce5f2a41aa4a04289
--- /dev/null
+++ b/examples/gas_dynamics/euler/vortex2.py
@@ -0,0 +1,154 @@
+# Copyright (C) 2008 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
+
+import pyopencl as cl # noqa
+
+from grudge import sym, bind, DGDiscretizationWithBoundaries
+
+def main(write_output=True, visualize=False):
+ from pytools import add_python_path_relative_to_script
+ add_python_path_relative_to_script("..")
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
+
+
+ from pytools.convergence import EOCRecorder
+ eoc_rec = EOCRecorder()
+
+ from meshmode.mesh.generation import generate_regular_rect_mesh
+
+
+ for order in [5]:
+ mesh = generate_regular_rect_mesh(a=(0,-5), b=(10,5), n=(5,5), order=order)
+ from gas_dynamics_initials import Vortex
+ flow = Vortex() # NOT ACTUALLY A VORTEX, constant velocity
+
+ from grudge.models.euler2 import EulerOperator
+ from grudge.models.gas_dynamics import (
+ GasDynamicsOperator, PolytropeEOS, GammaLawEOS)
+
+ from meshmode.mesh import BTAG_ALL
+ from meshmode.discretization.poly_element import QuadratureSimplexGroupFactory
+ # works equally well for GammaLawEOS
+ op = EulerOperator(dimensions=2, mu=flow.mu,
+
+ equation_of_state=PolytropeEOS(flow.gamma),
+ bc_inflow=flow.volume_interpolant(),
+ inflow_tag=BTAG_ALL)
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx,mesh, order=order,
+ quad_tag_to_group_factory={
+ "product": QuadratureSimplexGroupFactory(order=3*order)
+ })
+
+ if visualize:
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+
+
+ bound_flow_vol = bind(discr, flow.volume_interpolant())
+ fields = bound_flow_vol(queue, t=0)
+ print(fields)
+
+ euler_ex = bind(discr, op.sym_operator())
+
+ max_eigval = [0]
+ def rhs(t, q):
+ #ode_rhs, speed = euler_ex(queue, t=t, q=q)
+ return euler_ex(queue, t=t, q=q)
+ ode_rhs = ting[:-1]
+ speed = ting[-1]
+ #pu.db
+ max_eigval[0] = speed
+ return ode_rhs
+ rhs(0, fields)
+
+ from grudge.shortcuts import set_up_rk4
+ dt = 0.01
+ final_t = 2
+ dt_stepper = set_up_rk4("q", dt, fields, rhs)
+
+ if visualize:
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+
+ norm = bind(discr, sym.norm(2, sym.var("q")))
+
+ vis.write_vtk_file("fld-sinewave-%d-%04d.vtu" % (order, 0),
+ [
+ ("rho", op.rho(fields)),
+ ("e", op.e(fields)),
+ ("rho_u", op.rho_u(fields)),
+ ("u", op.u(fields))
+ #("true_rho", op.rho(true_fields)),
+ #("true_e", op.e(true_fields)),
+ #("true_rho_u", op.rho_u(true_fields)),
+ #("true_u", op.u(true_fields)
+ #("rhs_rho", op.rho(rhs_fields)),
+ #("rhs_e", op.e(rhs_fields)),
+ #("rhs_rho_u", op.rho_u(rhs_fields)),
+ ])
+ step = 1
+ for event in dt_stepper.run(t_end=final_t):
+ if isinstance(event, dt_stepper.StateComputed):
+ assert event.component_id == "q"
+ fields = event.state_component
+
+ if visualize:
+ vis.write_vtk_file("fld-sinewave-%d-%04d.vtu" % (order, step),
+ [
+ ("rho", op.rho(fields)),
+ ("e", op.e(fields)),
+ ("rho_u", op.rho_u(fields)),
+ ("u", op.u(fields)),
+
+ #("true_rho", op.rho(true_fields)),
+ #("true_e", op.e(true_fields)),
+ #("true_rho_u", op.rho_u(true_fields)),
+ #("true_u", op.u(true_fields)),
+
+ #("rhs_rho", op.rho(rhs_fields)),
+ #("rhs_e", op.e(rhs_fields)),
+ #("rhs_rho_u", op.rho_u(rhs_fields)),
+ ])
+ step += 1
+ print(step)
+
+
+
+ #true_fields = bound_flow_vol(queue, t=final_t)
+ #l2_error = norm(queue, q = fields-true_fields)
+ #l2_error_rho = discr.norm(op.rho(fields)-op.rho(true_fields))
+ #l2_error_e = discr.norm(op.e(fields)-op.e(true_fields))
+ #l2_error_rhou = discr.norm(op.rho_u(fields)-op.rho_u(true_fields))
+ #l2_error_u = discr.norm(op.u(fields)-op.u(true_fields))
+
+ #eoc_rec.add_data_point(order, l2_error)
+ #print(eoc_rec.pretty_print("P.Deg.", "L2 Error"))
+
+
+
+ # after order loop
+ assert eoc_rec.estimate_order_of_convergence()[0,1] > 6
+
+if __name__ == "__main__":
+ main(visualize=True)
diff --git a/examples/gas_dynamics/gas_dynamics_initials.py b/examples/gas_dynamics/gas_dynamics_initials.py
index 05ab91a314415a80d8a30452e2d68c3e26d6d378..98cf671ba37ef21e672db095e6e04a8b3a7dfaae 100644
--- a/examples/gas_dynamics/gas_dynamics_initials.py
+++ b/examples/gas_dynamics/gas_dynamics_initials.py
@@ -21,6 +21,7 @@ from __future__ import absolute_import
import numpy
import numpy.linalg as la
from six.moves import range
+from grudge import sym
@@ -120,83 +121,39 @@ class UniformMachFlow:
tag=tag, kind=discr.compute_kind,
dtype=discr.default_scalar_type)
-class Vortex:
- def __init__(self):
- self.beta = 5
- self.gamma = 1.4
- self.center = numpy.array([5, 0])
- self.velocity = numpy.array([1, 0])
- self.mu = 0
- self.prandtl = 0.72
- self.spec_gas_const = 287.1
- def __call__(self, t, x_vec):
- vortex_loc = self.center + t*self.velocity
-
- # coordinates relative to vortex center
- x_rel = x_vec[0] - vortex_loc[0]
- y_rel = x_vec[1] - vortex_loc[1]
- # Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
- # also JSH/TW Nodal DG Methods, p. 209
- from math import pi
- r = numpy.sqrt(x_rel**2+y_rel**2)
- expterm = self.beta*numpy.exp(1-r**2)
- u = self.velocity[0] - expterm*y_rel/(2*pi)
- v = self.velocity[1] + expterm*x_rel/(2*pi)
- rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
- p = rho**self.gamma
- e = p/(self.gamma-1) + rho/2*(u**2+v**2)
- from grudge.tools import join_fields
- return join_fields(rho, e, rho*u, rho*v)
-
- def volume_interpolant(self, t, discr):
- return discr.convert_volume(
- self(t, discr.nodes.T
- .astype(discr.default_scalar_type)),
- kind=discr.compute_kind)
-
- def boundary_interpolant(self, t, discr, tag):
- return discr.convert_boundary(
- self(t, discr.get_boundary(tag).nodes.T
- .astype(discr.default_scalar_type)),
- tag=tag, kind=discr.compute_kind)
-
-
-
-
-
-
-
-class Vortex:
+class Vortex: # NOT ACTUALLY A VORTEX, constant velocity
def __init__(self):
self.beta = 5
self.gamma = 1.4
self.center = numpy.array([5, 0])
- self.velocity = numpy.array([1, 0])
+ self.velocity = numpy.array([0.3, 0.3])
self.final_time = 0.5
self.mu = 0
self.prandtl = 0.72
self.spec_gas_const = 287.1
- def __call__(self, t, x_vec):
+ def __call__(self):
+ x_vec = sym.nodes(2)
+ t = sym.var("t", sym.DD_SCALAR)
+ from pytools.obj_array import join_fields
vortex_loc = self.center + t*self.velocity
# coordinates relative to vortex center
x_rel = x_vec[0] - vortex_loc[0]
y_rel = x_vec[1] - vortex_loc[1]
-
- # Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
+# Y.C. Zhou, G.W. Wei / Journal of Computational Physics 189 (2003) 159
# also JSH/TW Nodal DG Methods, p. 209
from math import pi
- r = numpy.sqrt(x_rel**2+y_rel**2)
- expterm = self.beta*numpy.exp(1-r**2)
+ r = sym.sqrt(x_rel**2+y_rel**2)
+ expterm = self.beta*sym.exp(1-r**2)
u = self.velocity[0] - expterm*y_rel/(2*pi)
v = self.velocity[1] + expterm*x_rel/(2*pi)
rho = (1-(self.gamma-1)/(16*self.gamma*pi**2)*expterm**2)**(1/(self.gamma-1))
@@ -204,20 +161,10 @@ class Vortex:
e = p/(self.gamma-1) + rho/2*(u**2+v**2)
- from grudge.tools import join_fields
return join_fields(rho, e, rho*u, rho*v)
- def volume_interpolant(self, t, discr):
- return discr.convert_volume(
- self(t, discr.nodes.T
- .astype(discr.default_scalar_type)),
- kind=discr.compute_kind)
-
- def boundary_interpolant(self, t, discr, tag):
- return discr.convert_boundary(
- self(t, discr.get_boundary(tag).nodes.T
- .astype(discr.default_scalar_type)),
- tag=tag, kind=discr.compute_kind)
+ def volume_interpolant(self):
+ return self()
diff --git a/examples/heat/heat2.py b/examples/heat/heat2.py
new file mode 100644
index 0000000000000000000000000000000000000000..54306eedae3cb1454e9cb2f8ab9eb679e60acc7e
--- /dev/null
+++ b/examples/heat/heat2.py
@@ -0,0 +1,124 @@
+# 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 .
+
+
+import numpy as np
+import pyopencl as cl # noqa
+import pyopencl.array # noqa
+import pyopencl.clmath # noqa
+
+import pytest # noqa
+
+from pyopencl.tools import ( # noqa
+ pytest_generate_tests_for_pyopencl as pytest_generate_tests)
+
+import logging
+logger = logging.getLogger(__name__)
+
+from grudge import sym, bind, DGDiscretizationWithBoundaries
+
+import numpy.linalg as la
+
+
+
+
+def main(write_output=True, order=4):
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
+
+ dim = 2
+
+
+ from meshmode.mesh.generation import generate_regular_rect_mesh
+ mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5),
+ n=(20, 20), order=order)
+
+ dt_factor = 4
+ h = 1/20
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
+
+ c = np.array([0.1,0.1])
+ norm_c = la.norm(c)
+
+
+ flux_type = "central"
+
+
+ def f(x):
+ return sym.If(
+ sym.Comparison(sym.norm(x), ">", 0.1),
+ 1,2)
+
+ def u_analytic(x):
+ return f(x)
+
+ from grudge.models.diffusion import DiffusionOperator
+ from meshmode.mesh import BTAG_ALL, BTAG_NONE
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
+ op = DiffusionOperator(2,
+ dirichlet_tag="dirichlet",
+ dirichlet_bc=0,#bad
+ neumann_tag="neumann",
+ neumann_bc=1,#bad
+ )
+
+
+ bound_op = bind(discr, op.m_sym_operator())
+
+ u = bind(discr, u_analytic(sym.nodes(dim)))(queue)
+
+ def rhs(t, u):
+ return bound_op(queue, t=t, u=u)
+
+ final_time = 0.3
+
+ dt = dt_factor * h/order**2
+ nsteps = (final_time // dt) + 1
+ dt = final_time/nsteps + 1e-15
+
+
+ from grudge.shortcuts import set_up_rk4
+ dt_stepper = set_up_rk4("u", dt, u, rhs)
+
+ last_u = None
+
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
+
+ step = 0
+
+ norm = bind(discr, sym.norm(2, sym.var("u")))
+
+ for event in dt_stepper.run(t_end=final_time):
+ if isinstance(event, dt_stepper.StateComputed):
+
+ step += 1
+
+ #print(step, event.t, norm(queue, u=event.state_component[0]))
+ vis.write_vtk_file("fld-%04d.vtu" % step,
+ [ ("u", event.state_component) ])
+
+
+
+
+
+
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/examples/maxwell/cavities.py b/examples/maxwell/cavities.py
index 85a7c3dec0cef99b4a72fe9c23439c4212c0c32d..a8763e8461c8836c9cf1e04a93af2612e37c0a59 100644
--- a/examples/maxwell/cavities.py
+++ b/examples/maxwell/cavities.py
@@ -28,7 +28,7 @@ THE SOFTWARE.
import numpy as np
import pyopencl as cl
from grudge.shortcuts import set_up_rk4
-from grudge import sym, bind, Discretization
+from grudge import sym, bind, DGDiscretizationWithBoundaries
from grudge.models.em import get_rectangular_cavity_mode
@@ -43,7 +43,7 @@ def main(dims, write_output=True, order=4):
b=(1.0,)*dims,
n=(5,)*dims)
- discr = Discretization(cl_ctx, mesh, order=order)
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
if 0:
epsilon0 = 8.8541878176e-12 # C**2 / (N m**2)
diff --git a/examples/wave/wave-min.py b/examples/wave/wave-min.py
index 89f4bfa836abe91e305802a455fd19a729cac172..e366a99273b86c1cf25f726dc4fc422700266c8e 100644
--- a/examples/wave/wave-min.py
+++ b/examples/wave/wave-min.py
@@ -35,12 +35,12 @@ def main(write_output=True, order=4):
cl_ctx = cl.create_some_context()
queue = cl.CommandQueue(cl_ctx)
- dims = 3
+ dims = 2
from meshmode.mesh.generation import generate_regular_rect_mesh
mesh = generate_regular_rect_mesh(
a=(-0.5,)*dims,
b=(0.5,)*dims,
- n=(16,)*dims)
+ n=(9,)*dims)
if mesh.dim == 2:
dt = 0.04
@@ -114,7 +114,7 @@ def main(write_output=True, order=4):
print(step, event.t, norm(queue, u=event.state_component[0]),
time()-t_last_step)
- if step % 10 == 0:
+ if step % 3 == 0:
vis.write_vtk_file("fld-%04d.vtu" % step,
[
("u", event.state_component[0]),
@@ -124,4 +124,4 @@ def main(write_output=True, order=4):
if __name__ == "__main__":
- main()
+ main(order = 4)
diff --git a/grudge/execution.py b/grudge/execution.py
index 2da2e508cc8ddf116eb4ffb30ecfd3f8835f957b..5c7228a6e9fddeb1a6710d33b174bb4d26c37acc 100644
--- a/grudge/execution.py
+++ b/grudge/execution.py
@@ -114,6 +114,7 @@ class ExecutionMapper(mappers.Evaluator,
cached_name += "abs"
else:
cached_name += expr.function.name
+ #print(cached_name + ":")
@memoize_in(self.bound_op, cached_name)
def knl():
@@ -128,6 +129,8 @@ class ExecutionMapper(mappers.Evaluator,
assert len(args) == 1
evt, (out,) = knl()(self.queue, a=args[0])
+ #print(out)
+
return out
def map_nodal_sum(self, op, field_expr):
@@ -233,15 +236,72 @@ class ExecutionMapper(mappers.Evaluator,
return result
def map_elementwise_max(self, op, field_expr):
- from grudge._internal import perform_elwise_max
field = self.rec(field_expr)
- out = self.discr.volume_zeros(dtype=field.dtype)
- for eg in self.discr.element_groups:
- perform_elwise_max(eg.ranges, field, out)
+ from grudge.tools import is_zero
+ if is_zero(field):
+ return 0
+
+ @memoize_in(self.bound_op, "elwise_max_knl")
+ def knl():
+ knl = lp.make_kernel(
+ """{[k,i,j]:
+ 0<=k boundary expr.
*volume_expr* will be None to query the Neumann condition.
@@ -328,19 +333,16 @@ class SecondDerivativeBase(object):
n_times = tgt.normal_times_flux
if tgt.strong_form:
- def adjust_flux(f):
- return n_times(u.int) - f
+ def adjust_flux(u, f,dd):
+ return n_times(u.int ,dd) - f
else:
- def adjust_flux(f):
+ def adjust_flux(u, f, dd):
return f
-
- from grudge.flux import FluxScalarPlaceholder
- u = FluxScalarPlaceholder()
+ int_u = sym.int_tpair(u)
tgt.add_derivative()
tgt.add_inner_fluxes(
- adjust_flux(self.grad_interior_flux(tgt, u)),
- tgt.int_flux_operand)
+ adjust_flux(int_u, self.grad_interior_flux(tgt, int_u), int_u.dd))
for tag in dirichlet_tags:
tgt.add_boundary_flux(
@@ -395,13 +397,12 @@ class LDGSecondDerivative(SecondDerivativeBase):
return np.array([self.beta_value]*tgt.dimensions, dtype=np.float64)
def grad_interior_flux(self, tgt, u):
- from grudge.symbolic.primitives import make_common_subexpression as cse
n_times = tgt.normal_times_flux
v_times = tgt.vec_times
return n_times(
- cse(u.avg, "u_avg")
- - v_times(self.beta(tgt), n_times(u.int-u.ext)))
+ sym.cse(u.avg, "u_avg")
+ - v_times(self.beta(tgt), n_times(u.int-u.ext, u.dd)), u.dd)
def div(self, tgt, bc_getter, dirichlet_tags, neumann_tags):
"""
@@ -409,7 +410,6 @@ class LDGSecondDerivative(SecondDerivativeBase):
*volume_expr* will be None to query the Neumann condition.
"""
- from grudge.symbolic.primitives import make_common_subexpression as cse
from grudge.flux import FluxVectorPlaceholder, PenaltyTerm
n_times = tgt.normal_times_flux
@@ -431,13 +431,13 @@ class LDGSecondDerivative(SecondDerivativeBase):
flux = n_times(flux_v.avg
+ v_times(self.beta(tgt),
- cse(n_times(flux_v.int - flux_v.ext), "jump_v"))
+ sym.cse(n_times(flux_v.int - flux_v.ext), "jump_v"))
- stab_term)
from pytools.obj_array import make_obj_array
- flux_arg_int = cse(make_obj_array(stab_term_generator.flux_args))
+ flux_arg_int = sym.cse(make_obj_array(stab_term_generator.flux_args))
- tgt.add_derivative(cse(tgt.operand))
+ tgt.add_derivative(sym.cse(tgt.operand))
tgt.add_inner_fluxes(adjust_flux(flux), flux_arg_int)
self.add_div_bcs(tgt, bc_getter, dirichlet_tags, neumann_tags,
@@ -459,9 +459,8 @@ class IPDGSecondDerivative(SecondDerivativeBase):
self.stab_coefficient = stab_coefficient
def grad_interior_flux(self, tgt, u):
- from grudge.symbolic.primitives import make_common_subexpression as cse
n_times = tgt.normal_times_flux
- return n_times(cse(u.avg, "u_avg"))
+ return n_times(sym.cse(u.avg, "u_avg"))
def div(self, tgt, bc_getter, dirichlet_tags, neumann_tags):
"""
@@ -469,7 +468,6 @@ class IPDGSecondDerivative(SecondDerivativeBase):
*volume_expr* will be None to query the Neumann condition.
"""
- from grudge.symbolic.primitives import make_common_subexpression as cse
from grudge.flux import FluxVectorPlaceholder, PenaltyTerm
n_times = tgt.normal_times_flux
@@ -496,9 +494,9 @@ class IPDGSecondDerivative(SecondDerivativeBase):
flux = n_times(pure_diff_v.avg - stab_term)
from pytools.obj_array import make_obj_array
- flux_arg_int = cse(make_obj_array(stab_term_generator.flux_args))
+ flux_arg_int = sym.cse(make_obj_array(stab_term_generator.flux_args))
- tgt.add_derivative(cse(tgt.operand))
+ tgt.add_derivative(sym.cse(tgt.operand))
tgt.add_inner_fluxes(adjust_flux(flux), flux_arg_int)
self.add_div_bcs(tgt, bc_getter, dirichlet_tags, neumann_tags,
diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py
index c555cea0a56e947b4a94e0cd1736e8fa2b97ce63..063db1b534e4943161e5e0c45f88cb6a828f2000 100644
--- a/grudge/symbolic/compiler.py
+++ b/grudge/symbolic/compiler.py
@@ -103,10 +103,11 @@ class LoopyKernelInstruction(Instruction):
@memoize_method
def get_dependencies(self):
from pymbolic.primitives import Variable, Subscript
+ dep_mapper = _make_dep_mapper(include_subscripts=False)
return set(
- v
+ dep
for v in self.kernel_descriptor.input_mappings.values()
- if isinstance(v, (Variable, Subscript)))
+ for dep in dep_mapper(v))
def __str__(self):
knl_str = "\n".join(
@@ -493,6 +494,12 @@ class Code(object):
if pre_assign_check is not None:
pre_assign_check(target, value)
+ import pyopencl.array
+ import numpy as np
+ if isinstance(value, pyopencl.array.Array):
+ if np.isnan(value.get()).any():
+ print(value.get())
+ pu.db
context[target] = value
futures.extend(new_futures)
diff --git a/grudge/symbolic/dofdesc_inference.py b/grudge/symbolic/dofdesc_inference.py
index 832c6a0371764cceef497e67a4a9c27300a5825a..942b805b622b38663f8ba75403eaedc6c4190bb0 100644
--- a/grudge/symbolic/dofdesc_inference.py
+++ b/grudge/symbolic/dofdesc_inference.py
@@ -99,6 +99,8 @@ class DOFDescInferenceMapper(RecursiveMapper, CSECachingMapperMixin):
self.name_to_dofdesc = name_to_dofdesc
def infer_for_name(self, name):
+ #if(name=='expr_29'):
+ #pu.db
try:
return self.name_to_dofdesc[name]
except KeyError:
@@ -157,6 +159,10 @@ class DOFDescInferenceMapper(RecursiveMapper, CSECachingMapperMixin):
def map_nodal_sum(self, expr, enclosing_prec):
return DOFDesc(DTAG_SCALAR)
+ def map_max(self, expr):
+ return self.map_multi_child(expr, expr.children)
+
+
map_nodal_max = map_nodal_sum
map_nodal_min = map_nodal_sum
diff --git a/grudge/symbolic/mappers/__init__.py b/grudge/symbolic/mappers/__init__.py
index ca11cbf0d92c96b6c9d00a6e035b7b840c2a85cd..59e4b0843cc003b421d7c09b379d3df88030fbbd 100644
--- a/grudge/symbolic/mappers/__init__.py
+++ b/grudge/symbolic/mappers/__init__.py
@@ -135,6 +135,7 @@ class OperatorReducerMixin(LocalOpReducerMixin, FluxOpReducerMixin):
map_nodal_sum = _map_op_base
map_nodal_min = _map_op_base
map_nodal_max = _map_op_base
+ map_elementwise_max = _map_op_base
map_stiffness = _map_op_base
map_diff = _map_op_base
@@ -183,6 +184,7 @@ class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin):
map_nodal_sum = map_elementwise_linear
map_nodal_min = map_elementwise_linear
map_nodal_max = map_elementwise_linear
+ map_elementwise_max = map_elementwise_linear
map_stiffness = map_elementwise_linear
map_diff = map_elementwise_linear
@@ -515,19 +517,23 @@ class GlobalToReferenceMapper(CSECachingMapperMixin, IdentityMapper):
if with_jacobian:
rec_field = jac_tag * rec_field
return sum(
+ ref_class(rst_axis, dd_in=dd_in)(
sym.inverse_metric_derivative(
rst_axis, expr.op.xyz_axis,
- ambient_dim=self.ambient_dim, dim=self.dim) *
- ref_class(rst_axis, dd_in=dd_in)(rec_field)
- for rst_axis in range(self.dim))
+ ambient_dim=self.ambient_dim, dim=self.dim, dd=dd_in) * rec_field)
+ for rst_axis in range(self.dim))
if isinstance(expr.op, op.MassOperator):
return op.RefMassOperator(expr.op.dd_in, expr.op.dd_out)(
jac_in * self.rec(expr.field))
elif isinstance(expr.op, op.InverseMassOperator):
- return op.RefInverseMassOperator(expr.op.dd_in, expr.op.dd_out)(
- 1/jac_in * self.rec(expr.field))
+ # Chan, Hewett, and Warburton. "Weight-Adjusted Discontinuous
+ # Galerkin Methods: Curvilinear Meshes." Siam Journal on Scientific Computing
+ in_inv_op = op.RefInverseMassOperator(expr.op.dd_in, expr.op.dd_in)
+ trans_mass_op = op.RefMassOperator(expr.op.dd_in, expr.op.dd_out)
+ out_inv_op = op.RefInverseMassOperator(expr.op.dd_out, expr.op.dd_out)
+ return out_inv_op(trans_mass_op(in_inv_op(self.rec(expr.field))/jac_in))
elif isinstance(expr.op, op.FaceMassOperator):
jac_in_surf = sym.area_element(self.ambient_dim, self.dim - 1,
@@ -610,6 +616,9 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper):
def map_nodal_min(self, expr, enclosing_prec):
return "NodalMin" + self._format_op_dd(expr)
+ def map_elementwise_max(self, expr, enclosing_prec):
+ return "ElementwiseMax" + self._format_op_dd(expr)
+
# }}}
# {{{ global differentiation
@@ -718,7 +727,13 @@ class NoCSEStringifyMapper(StringifyMapper):
# {{{ quadrature support
class QuadratureCheckerAndRemover(CSECachingMapperMixin, IdentityMapper):
- """Checks whether all quadratu
+ """ Checks that all quadrature tags exist in qttg (quad_tag_to_group_factory),
+ and then removes all quadrature changes that are None in qttg.
+
+ For example: if you have an expression that interpolates to a QTAG called
+ "product", and the discretization is made with qttg = {"product": None},
+ then all operators will be changed to ignore "product" QTAG.
+
"""
def __init__(self, quad_tag_to_group_factory):
@@ -729,6 +744,7 @@ class QuadratureCheckerAndRemover(CSECachingMapperMixin, IdentityMapper):
map_common_subexpression_uncached = \
IdentityMapper.map_common_subexpression
+ #
def _process_dd(self, dd, location_descr):
from grudge.symbolic.primitives import DOFDesc, QTAG_NONE
if dd.quadrature_tag is not QTAG_NONE:
@@ -779,6 +795,58 @@ class QuadratureCheckerAndRemover(CSECachingMapperMixin, IdentityMapper):
dd = self._process_dd(expr.dd, location_descr=type(expr).__name__)
return type(expr)(expr.axis, dd)
+class QuadratureRemover(CSECachingMapperMixin, IdentityMapper):
+ """ Removes all quadrature use
+ """
+
+ def __init__(self):
+ IdentityMapper.__init__(self)
+ CSECachingMapperMixin.__init__(self)
+
+ map_common_subexpression_uncached = \
+ IdentityMapper.map_common_subexpression
+
+ def _process_dd(self, dd):
+ from grudge.symbolic.primitives import DOFDesc, QTAG_NONE
+ return DOFDesc(dd.domain_tag, QTAG_NONE)
+
+ def map_operator_binding(self, expr):
+ dd_in_orig = dd_in = expr.op.dd_in
+ dd_out_orig = dd_out = expr.op.dd_out
+
+ dd_in = self._process_dd(dd_in)
+ dd_out = self._process_dd(dd_out)
+
+ if dd_in_orig == dd_in and dd_out_orig == dd_out:
+ # unchanged
+ return IdentityMapper.map_operator_binding(self, expr)
+
+ import grudge.symbolic.operators as op
+ # changed
+
+ if dd_in == dd_out and isinstance(expr.op, op.InterpolationOperator):
+ # This used to be to-quad interpolation and has become a no-op.
+ # Remove it.
+ return self.rec(expr.field)
+
+ if isinstance(expr.op, op.StiffnessTOperator):
+ new_op = type(expr.op)(expr.op.xyz_axis, dd_in, dd_out)
+ elif isinstance(expr.op, (op.FaceMassOperator, op.InterpolationOperator)):
+ new_op = type(expr.op)(dd_in, dd_out)
+ else:
+ raise NotImplementedError("do not know how to modify dd_in and dd_out "
+ "in %s" % type(expr.op).__name__)
+
+ return new_op(self.rec(expr.field))
+
+ #def map_ones(self, expr):
+ #dd = self._process_dd(expr.dd, location_descr=type(expr).__name__)
+ #return type(expr)(dd)
+
+ #def map_node_coordinate_component(self, expr):
+ #dd = self._process_dd(expr.dd, location_descr=type(expr).__name__)
+ #return type(expr)(expr.axis, dd)
+
# }}}
diff --git a/grudge/symbolic/operators.py b/grudge/symbolic/operators.py
index 194b071614dba86a7ff150c075e5a3c987f23a4b..31dc316693ac02cece0e1bb8b893e4f985745ddd 100644
--- a/grudge/symbolic/operators.py
+++ b/grudge/symbolic/operators.py
@@ -57,6 +57,10 @@ class Operator(pymbolic.primitives.Expression):
def __init__(self, dd_in, dd_out):
self.dd_in = _sym().as_dofdesc(dd_in)
self.dd_out = _sym().as_dofdesc(dd_out)
+ #TODO: remove this. According to Python docs this creates weird circular references
+ # And a bad time for everyone
+ #import inspect
+ #self.stack = inspect.stack()
def stringifier(self):
from grudge.symbolic.mappers import StringifyMapper
@@ -113,7 +117,7 @@ interp = InterpolationOperator
class ElementwiseMaxOperator(Operator):
mapper_method = intern("map_elementwise_max")
-
+
# {{{ nodal reduction: sum, integral, max
diff --git a/grudge/symbolic/primitives.py b/grudge/symbolic/primitives.py
index 00f7b8d386af6130810cf900bbaaebb0027f5adc..a29e481aa4e8253da7a7ae13e18234343507c2f3 100644
--- a/grudge/symbolic/primitives.py
+++ b/grudge/symbolic/primitives.py
@@ -198,6 +198,13 @@ class DOFDesc(object):
self.domain_tag = domain_tag
self.quadrature_tag = quadrature_tag
+
+ #TODO: remove this. According to Python docs this creates weird circular references
+ # And a bad time for everyone
+ #import inspect
+ #self.stack = inspect.stack()
+
+
def is_scalar(self):
return self.domain_tag is DTAG_SCALAR
@@ -311,6 +318,7 @@ class Variable(HasDOFDesc, ExpressionBase, pymbolic.primitives.Variable):
HasDOFDesc.__init__(self, dd)
pymbolic.primitives.Variable.__init__(self, name)
+
def __getinitargs__(self):
return (self.name, self.dd,)
diff --git a/test/test_grudge.py b/test/test_grudge.py
index 34532bfc80371e29bde28ea9674c48e36ab889aa..3976f246c6f1714a10730695df4cc52850657ed6 100644
--- a/test/test_grudge.py
+++ b/test/test_grudge.py
@@ -333,6 +333,80 @@ 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])
+def test_convergence_curvi_advection(ctx_factory, order):
+ """Test whether a curvilinear mesh converges"""
+
+ cl_ctx = cl.create_some_context()
+ queue = cl.CommandQueue(cl_ctx)
+
+ from pytools.convergence import EOCRecorder
+ eoc_rec = EOCRecorder()
+
+ dims = 2
+ ns = [4, 6, 8]
+ for n in ns:
+
+ from meshmode.mesh.generation import generate_warped_rect_mesh
+ mesh = generate_warped_rect_mesh(dims, order, n)
+
+ discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)
+
+ advec_v = np.array([0.1, 0.1])
+ norm_v = la.norm(advec_v)
+
+ def f(x):
+ return sym.sin(3*x)
+
+ def u_analytic(x):
+ return f(-np.dot(advec_v, x)/norm_v+sym.var("t", sym.DD_SCALAR)*norm_v)
+
+ from grudge.models.advection import WeakAdvectionOperator
+ analytic_sol = bind(discr, u_analytic(sym.nodes(dims)))
+
+ analytic_init = bind(discr, u_analytic(sym.nodes(dims)))
+
+ fields = analytic_init(queue, t=0)
+
+ op = WeakAdvectionOperator(advec_v,
+ inflow_u=u_analytic(sym.nodes(dims, sym.BTAG_ALL)),
+ flux_type="central")
+
+ bound_op = bind(discr, op.sym_operator())
+
+ def rhs(t, u):
+ return bound_op(queue, t=t, u=u)
+
+ final_t = 0.1
+
+ h = 1.0/n
+ dt_factor = 4
+ dt = dt_factor * h/order**2
+ nsteps = (final_t // dt) + 1
+ dt = final_t/nsteps + 1e-15
+
+ from grudge.shortcuts import set_up_rk4
+ dt_stepper = set_up_rk4("u", dt, fields, rhs)
+
+ 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 == "u"
+ esc = event.state_component
+ step += 1
+
+ sol = analytic_sol(queue, t=step * dt)
+ total_error = norm(queue, u=(esc - sol)) / norm(queue, u=sol)
+ 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
+
+
@pytest.mark.parametrize("order", [3, 4, 5])
def test_convergence_maxwell(ctx_factory, order):
"""Test whether 3D maxwells actually converges"""