diff --git a/examples/advection/var-velocity.py b/examples/advection/var-velocity.py
index 83502851e8f0166fec28bcbb819fa91f7a93af86..8611b29d6e5e52f1ff45e31741fec2a2ad82d573 100644
--- a/examples/advection/var-velocity.py
+++ b/examples/advection/var-velocity.py
@@ -1,5 +1,5 @@
# grudge - the Hybrid'n'Easy DG Environment
-# Copyright (C) 2009 Andreas Stock
+# 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
@@ -15,246 +15,95 @@
# 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, Discretization
+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
+
+ 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=(15, 15), order=order)
+
+ dt_factor = 4
+ h = 1/20
+
+ discr = Discretization(cl_ctx, mesh, order=order)
+
+ sym_x = sym.nodes(2)
+
+ advec_v = join_fields(-1*sym_x[1], sym_x[0]) / 2
+
+ flux_type = "central"
+
+ source_center = np.array([0.1, 0.1])
+ source_width = 0.05
+
+ sym_x = sym.nodes(2)
+ sym_source_center_dist = sym_x - source_center
+
+ def f(x):
+ return sym.exp(
+ -np.dot(sym_source_center_dist, sym_source_center_dist)
+ / source_width**2)
+
+ def u_analytic(x):
+ return 0
-from __future__ import division
-from __future__ import absolute_import
-from __future__ import print_function
-import numpy
-
-
-
-def main(write_output=True, flux_type_arg="central", use_quadrature=True,
- final_time=20):
- from math import sin, cos, pi, sqrt
-
- from grudge.backends import guess_run_context
- rcon = guess_run_context()
-
- # mesh setup --------------------------------------------------------------
- if rcon.is_head_rank:
- #from grudge.mesh.generator import make_disk_mesh
- #mesh = make_disk_mesh()
- from grudge.mesh.generator import make_rect_mesh
- mesh = make_rect_mesh(a=(-1,-1),b=(1,1),max_area=0.008)
-
- if rcon.is_head_rank:
- mesh_data = rcon.distribute_mesh(mesh)
- else:
- mesh_data = rcon.receive_mesh()
-
- # space-time-dependent-velocity-field -------------------------------------
- # simple vortex
- class TimeDependentVField:
- """ `TimeDependentVField` is a callable expecting `(x, t)` representing space and time
-
- `x` is of the length of the spatial dimension and `t` is the time."""
- shape = (2,)
-
- def __call__(self, pt, el, t):
- x, y = pt
- # Correction-Factor to make the speed zero on the on the boundary
- #fac = (1-x**2)*(1-y**2)
- fac = 1.
- return numpy.array([-y*fac, x*fac]) * cos(pi*t)
-
- class VField:
- """ `VField` is a callable expecting `(x)` representing space
-
- `x` is of the length of the spatial dimension."""
- shape = (2,)
-
- def __call__(self, pt, el):
- x, y = pt
- # Correction-Factor to make the speed zero on the on the boundary
- #fac = (1-x**2)*(1-y**2)
- fac = 1.
- return numpy.array([-y*fac, x*fac])
-
- # space-time-dependent State BC (optional)-----------------------------------
- class TimeDependentBc_u:
- """ space and time dependent BC for state u"""
- def __call__(self, pt, el, t):
- x, y = pt
- if t <= 0.5:
- if x > 0:
- return 1
- else:
- return 0
- else:
- return 0
-
- class Bc_u:
- """ Only space dependent BC for state u"""
- def __call__(seld, pt, el):
- x, y = pt
- if x > 0:
- return 1
- else:
- return 0
-
-
- # operator setup ----------------------------------------------------------
- # In the operator setup it is possible to switch between a only space
- # dependent velocity field `VField` or a time and space dependent
- # `TimeDependentVField`.
- # For `TimeDependentVField`: advec_v=TimeDependentGivenFunction(VField())
- # For `VField`: advec_v=TimeConstantGivenFunction(GivenFunction(VField()))
- # Same for the Bc_u Function! If you don't define Bc_u then the BC for u = 0.
-
- from grudge.data import \
- ConstantGivenFunction, \
- TimeConstantGivenFunction, \
- TimeDependentGivenFunction, \
- GivenFunction
from grudge.models.advection import VariableCoefficientAdvectionOperator
- op = VariableCoefficientAdvectionOperator(mesh.dimensions,
- #advec_v=TimeDependentGivenFunction(
- # TimeDependentVField()),
- advec_v=TimeConstantGivenFunction(
- GivenFunction(VField())),
- #bc_u_f=TimeDependentGivenFunction(
- # TimeDependentBc_u()),
- bc_u_f=TimeConstantGivenFunction(
- GivenFunction(Bc_u())),
- flux_type=flux_type_arg)
-
- # discretization setup ----------------------------------------------------
- order = 5
- if use_quadrature:
- quad_min_degrees = {"quad": 3*order}
- else:
- quad_min_degrees = {}
-
- discr = rcon.make_discretization(mesh_data, order=order,
- default_scalar_type=numpy.float64,
- debug=["cuda_no_plan"],
- quad_min_degrees=quad_min_degrees,
- tune_for=op.sym_operator(),
-
- )
- vis_discr = discr
-
- # visualization setup -----------------------------------------------------
- from grudge.visualization import VtkVisualizer
- if write_output:
- vis = VtkVisualizer(vis_discr, rcon, "fld")
-
- # initial condition -------------------------------------------------------
- if True:
- def initial(pt, el):
- # Gauss pulse
- from math import exp
- x = (pt-numpy.array([0.3, 0.5]))*8
- return exp(-numpy.dot(x, x))
- else:
- def initial(pt, el):
- # Rectangle
- x, y = pt
- if abs(x) < 0.5 and abs(y) < 0.2:
- return 2
- else:
- return 1
-
- u = discr.interpolate_volume_function(initial)
-
- # timestep setup ----------------------------------------------------------
- from grudge.timestep.runge_kutta import LSRK4TimeStepper
- stepper = LSRK4TimeStepper(
- vector_primitive_factory=discr.get_vector_primitive_factory())
-
- if rcon.is_head_rank:
- print("%d elements" % len(discr.mesh.elements))
-
- # filter setup-------------------------------------------------------------
- from grudge.discretization import ExponentialFilterResponseFunction
- from grudge.symbolic.operators import FilterOperator
- mode_filter = FilterOperator(
- ExponentialFilterResponseFunction(min_amplification=0.9,order=4))\
- .bind(discr)
-
- # diagnostics setup -------------------------------------------------------
- from pytools.log import LogManager, \
- add_general_quantities, \
- add_simulation_quantities, \
- add_run_info
-
- if write_output:
- log_file_name = "space-dep.dat"
- 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)
-
- from grudge.log import Integral, LpNorm
- u_getter = lambda: u
- logmgr.add_quantity(Integral(u_getter, discr, name="int_u"))
- logmgr.add_quantity(LpNorm(u_getter, discr, p=1, name="l1_u"))
- logmgr.add_quantity(LpNorm(u_getter, discr, name="l2_u"))
-
- logmgr.add_watches(["step.max", "t_sim.max", "l2_u", "t_step.max"])
-
- # Initialize v for data output:
- v = op.advec_v.volume_interpolant(0, discr)
-
- # timestep loop -----------------------------------------------------------
- rhs = op.bind(discr)
- 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=u))
-
- for step, t, dt in step_it:
- if step % 10 == 0 and write_output:
- visf = vis.make_file("fld-%04d" % step)
- vis.add_data(visf, [
- ("u", discr.convert_volume(u, kind="numpy")),
- ("v", discr.convert_volume(v, kind="numpy"))
- ], time=t, step=step)
- visf.close()
-
- u = stepper(u, t, dt, rhs)
-
- # We're feeding in a discontinuity through the BCs.
- # Quadrature does not help with shock capturing--
- # therefore we do need to filter here, regardless
- # of whether quadrature is enabled.
- u = mode_filter(u)
-
- assert discr.norm(u) < 10
-
- finally:
- if write_output:
- vis.close()
-
- logmgr.close()
- discr.close()
+ discr = Discretization(cl_ctx, mesh, order=order)
+ op = VariableCoefficientAdvectionOperator(2, advec_v,
+ inflow_u=u_analytic(sym.nodes(dim, sym.BTAG_ALL)),
+ flux_type=flux_type)
+ bound_op = bind(discr, op.sym_operator())
-if __name__ == "__main__":
- main()
+ u = bind(discr, f(sym.nodes(dim)))(queue, t=0)
+
+ def rhs(t, u):
+ return bound_op(queue, t=t, u=u)
+ final_time = 2
+ 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)
-# entry points for py.test ----------------------------------------------------
-def test_var_velocity_advection():
- from pytools.test import mark_test
- mark_long = mark_test.long
+ from grudge.shortcuts import make_visualizer
+ vis = make_visualizer(discr, vis_order=order)
- for flux_type in ["upwind", "central", "lf"]:
- for use_quadrature in [False, True]:
- descr = "variable-velocity-advection with %s flux" % flux_type
- if use_quadrature:
- descr += " and quadrature"
+ step = 0
- yield descr, mark_long(main), False, flux_type, use_quadrature, 1
+ 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/advection/weak.py b/examples/advection/weak.py
new file mode 100644
index 0000000000000000000000000000000000000000..de4bc4d3e0d841a4e0705803c8be71e2898700f6
--- /dev/null
+++ b/examples/advection/weak.py
@@ -0,0 +1,119 @@
+# grudge - the Hybrid'n'Easy DG Environment
+# 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, Discretization
+
+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 = Discretization(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.sin(3*x)
+
+ def u_analytic(x):
+ return f(-np.dot(c, x)/norm_c+sym.var("t", sym.DD_SCALAR)*norm_c)
+
+ from grudge.models.advection import WeakAdvectionOperator
+ from meshmode.mesh import BTAG_ALL, BTAG_NONE
+
+ discr = Discretization(cl_ctx, mesh, order=order)
+ op = WeakAdvectionOperator(c,
+ inflow_u=u_analytic(sym.nodes(dim, sym.BTAG_ALL)),
+ flux_type=flux_type)
+
+ bound_op = bind(discr, op.sym_operator())
+
+ u = bind(discr, u_analytic(sym.nodes(dim)))(queue, t=0)
+
+ 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/grudge/models/advection.py b/grudge/models/advection.py
index 8fda51a0a3b98a3a3ac04aea974cae7350904e33..1c83ed9675d5a45c75b22e3b4d5ca7926acf9133 100644
--- a/grudge/models/advection.py
+++ b/grudge/models/advection.py
@@ -30,7 +30,6 @@ import numpy as np
import numpy.linalg as la
from grudge.models import HyperbolicOperator
-from grudge.models.second_order import CentralSecondDerivative
from grudge import sym
@@ -133,216 +132,55 @@ class WeakAdvectionOperator(AdvectionOperatorBase):
# {{{ variable-coefficient advection
class VariableCoefficientAdvectionOperator(HyperbolicOperator):
- """A class for space- and time-dependent DG advection operators.
-
- :param advec_v: Adheres to the :class:`grudge.data.ITimeDependentGivenFunction`
- interfacer and is an n-dimensional vector representing the velocity.
- :param bc_u_f: Adheres to the :class:`grudge.data.ITimeDependentGivenFunction`
- interface and is a scalar representing the boundary condition at all
- boundary faces.
-
- Optionally allows diffusion.
- """
-
- flux_types = [
- "central",
- "upwind",
- "lf"
- ]
-
- def __init__(self,
- dimensions,
- advec_v,
- bc_u_f="None",
- flux_type="central",
- diffusion_coeff=None,
- diffusion_scheme=CentralSecondDerivative()):
- self.dimensions = dimensions
- self.advec_v = advec_v
- self.bc_u_f = bc_u_f
+ def __init__(self, dim, v, inflow_u, flux_type="central"):
+ self.ambient_dim = dim
+ self.v = v
+ self.inflow_u = inflow_u
self.flux_type = flux_type
- self.diffusion_coeff = diffusion_coeff
- self.diffusion_scheme = diffusion_scheme
-
- # {{{ flux ----------------------------------------------------------------
- def flux(self):
- from grudge.flux import (
- make_normal,
- FluxVectorPlaceholder,
- flux_max)
- from pymbolic.primitives import IfPositive
- d = self.dimensions
+ def flux(self, u):
+ normal = sym.normal(u. dd, self.ambient_dim)
- w = FluxVectorPlaceholder((1+d)+1)
- u = w[0]
- v = w[1:d+1]
- c = w[1+d]
+ surf_v = sym.interp("vol", u.dd)(self.v)
- normal = make_normal(self.dimensions)
+ v_dot_normal = sym.cse(np.dot(surf_v, normal), "v_dot_normal")
+ norm_v = sym.sqrt(np.sum(self.v**2))
if self.flux_type == "central":
- return (u.int*np.dot(v.int, normal)
- + u.ext*np.dot(v.ext, normal)) * 0.5
- elif self.flux_type == "lf":
- n_vint = np.dot(normal, v.int)
- n_vext = np.dot(normal, v.ext)
- return 0.5 * (n_vint * u.int + n_vext * u.ext) \
- - 0.5 * (u.ext - u.int) \
- * flux_max(c.int, c.ext)
+ return u.avg*v_dot_normal
+ elif self.flux_type == "lf":
+ return u.avg*v_dot_normal + 0.5*norm_v*(u.int - u.ext)
elif self.flux_type == "upwind":
return (
- IfPositive(
- np.dot(normal, v.avg),
- np.dot(normal, v.int) * u.int, # outflow
- np.dot(normal, v.ext) * u.ext, # inflow
+ v_dot_normal * sym.If(
+ sym.Comparison(v_dot_normal, ">", 0),
+ u.int, # outflow
+ u.ext, # inflow
))
else:
raise ValueError("invalid flux type")
- # }}}
-
- def bind_characteristic_velocity(self, discr):
- from grudge.symbolic.operators import (
- ElementwiseMaxOperator)
- from grudge.symbolic import make_sym_vector
- velocity_vec = make_sym_vector("v", self.dimensions)
- velocity = ElementwiseMaxOperator()(
- np.dot(velocity_vec, velocity_vec)**0.5)
-
- compiled = discr.compile(velocity)
-
- def do(t, u):
- return compiled(v=self.advec_v.volume_interpolant(t, discr))
-
- return do
-
- def sym_operator(self, with_sensor=False):
- # {{{ operator preliminaries ------------------------------------------
- from grudge.symbolic import (Field, BoundaryPair, get_flux_operator,
- make_stiffness_t, InverseMassOperator, make_sym_vector,
- ElementwiseMaxOperator, RestrictToBoundary)
-
- from grudge.symbolic.primitives import make_common_subexpression as cse
-
- from grudge.symbolic.operators import (
- QuadratureGridUpsampler,
- QuadratureInteriorFacesGridUpsampler)
-
- to_quad = QuadratureGridUpsampler("quad")
- to_if_quad = QuadratureInteriorFacesGridUpsampler("quad")
-
- from grudge.tools import join_fields, \
- ptwise_dot
-
- u = Field("u")
- v = make_sym_vector("v", self.dimensions)
- c = ElementwiseMaxOperator()(ptwise_dot(1, 1, v, v))
-
- quad_u = cse(to_quad(u))
- quad_v = cse(to_quad(v))
-
- w = join_fields(u, v, c)
- quad_face_w = to_if_quad(w)
- # }}}
-
- # {{{ boundary conditions ---------------------------------------------
-
- from grudge.mesh import BTAG_ALL
- bc_c = to_quad(RestrictToBoundary(BTAG_ALL)(c))
- bc_u = to_quad(Field("bc_u"))
- bc_v = to_quad(RestrictToBoundary(BTAG_ALL)(v))
- if self.bc_u_f is "None":
- bc_w = join_fields(0, bc_v, bc_c)
- else:
- bc_w = join_fields(bc_u, bc_v, bc_c)
-
- minv_st = make_stiffness_t(self.dimensions)
- m_inv = InverseMassOperator()
-
- flux_op = get_flux_operator(self.flux())
- # }}}
-
- # {{{ diffusion -------------------------------------------------------
- if with_sensor or (
- self.diffusion_coeff is not None and self.diffusion_coeff != 0):
- if self.diffusion_coeff is None:
- diffusion_coeff = 0
- else:
- diffusion_coeff = self.diffusion_coeff
-
- if with_sensor:
- diffusion_coeff += Field("sensor")
-
- from grudge.second_order import SecondDerivativeTarget
-
- # strong_form here allows IPDG to reuse the value of grad u.
- grad_tgt = SecondDerivativeTarget(
- self.dimensions, strong_form=True,
- operand=u)
-
- self.diffusion_scheme.grad(grad_tgt, bc_getter=None,
- dirichlet_tags=[], neumann_tags=[])
-
- div_tgt = SecondDerivativeTarget(
- self.dimensions, strong_form=False,
- operand=diffusion_coeff*grad_tgt.minv_all)
-
- self.diffusion_scheme.div(div_tgt,
- bc_getter=None,
- dirichlet_tags=[], neumann_tags=[])
-
- diffusion_part = div_tgt.minv_all
- else:
- diffusion_part = 0
-
- # }}}
-
- to_quad = QuadratureGridUpsampler("quad")
- quad_u = cse(to_quad(u))
- quad_v = cse(to_quad(v))
-
- return m_inv(np.dot(minv_st, cse(quad_v*quad_u))
- - (flux_op(quad_face_w)
- + flux_op(BoundaryPair(quad_face_w, bc_w, BTAG_ALL)))) \
- + diffusion_part
-
- def bind(self, discr, sensor=None):
- compiled_sym_operator = discr.compile(
- self.sym_operator(with_sensor=sensor is not None))
-
- from grudge.mesh import check_bc_coverage, BTAG_ALL
- check_bc_coverage(discr.mesh, [BTAG_ALL])
-
- def rhs(t, u):
- kwargs = {}
- if sensor is not None:
- kwargs["sensor"] = sensor(t, u)
-
- if self.bc_u_f is not "None":
- kwargs["bc_u"] = \
- self.bc_u_f.boundary_interpolant(t, discr, tag=BTAG_ALL)
-
- return compiled_sym_operator(
- u=u,
- v=self.advec_v.volume_interpolant(t, discr),
- **kwargs)
+ def sym_operator(self):
+ u = sym.var("u")
- return rhs
+ # boundary conditions -------------------------------------------------
+ bc_in = self.inflow_u
- def max_eigenvalue(self, t, fields=None, discr=None):
- # Gives the max eigenvalue of a vector of eigenvalues.
- # As the velocities of each node is stored in the velocity-vector-field
- # a pointwise dot product of this vector has to be taken to get the
- # magnitude of the velocity at each node. From this vector the maximum
- # values limits the timestep.
+ def flux(pair):
+ return sym.interp(pair.dd, "all_faces")(
+ self.flux(pair))
- from grudge.tools import ptwise_dot
- v = self.advec_v.volume_interpolant(t, discr)
- return discr.nodewise_max(ptwise_dot(1, 1, v, v)**0.5)
+ return sym.InverseMassOperator()(
+ np.dot(sym.stiffness_t(self.ambient_dim), sym.cse(self.v*u))
+ - sym.FaceMassOperator()(
+ flux(sym.int_tpair(u))
+ + flux(sym.bv_tpair(sym.BTAG_ALL, u, bc_in))
+ # FIXME: Add back support for inflow/outflow tags
+ #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in))
+ #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out))
+ ))
# }}}
-
# vim: foldmethod=marker