diff --git a/examples/advection/var-velocity.py b/examples/advection/var-velocity.py index ddc7535288cd82c1bbc56cb8decd9b0644cea73a..0a69967e8613adc76a9ff1d93ba02db90fb2dc9a 100644 --- a/examples/advection/var-velocity.py +++ b/examples/advection/var-velocity.py @@ -37,12 +37,13 @@ def main(write_output=True, order=4): dim = 2 + n_divs = 15 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) + n=(n_divs, n_divs), order=order) - dt_factor = 4 - h = 1/20 + dt_factor = 5 + h = 1/n_divs discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order) diff --git a/examples/quad/flux_quad.py b/examples/quad/flux_quad.py new file mode 100644 index 0000000000000000000000000000000000000000..3d7f9a322d39176626f39bd1ea19267e0d42a43a --- /dev/null +++ b/examples/quad/flux_quad.py @@ -0,0 +1,91 @@ +# 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 <http://www.gnu.org/licenses/>. + + +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(order=1): + cl_ctx = cl.create_some_context() + queue = cl.CommandQueue(cl_ctx) + + dim = 2 + + + from meshmode.mesh.generation import n_gon, generate_regular_rect_mesh + #mesh = n_gon(3,np.array([0.1,0.2,0.4,0.5,0.6,0.7,0.9])) + mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5), + n=(3, 3), order=7) + + + + + pquad_dd = sym.DOFDesc("vol", "product") + + discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order, quad_min_degrees={"product": 2 * order} ) + + + magic_thing = sym.DOFDesc(sym.FACE_RESTR_ALL, "product") + #magic_thing = sym.DOFDesc(sym.FACE_RESTR_ALL) + def fluxy_stuff(u): + return sym.interp(u.dd, magic_thing)(u.int) + + + ones = bind(discr, sym.nodes(dim)[0] / sym.nodes(dim)[0])(queue, t=0) + nds = sym.nodes(dim) + op = sym.FaceMassOperator(magic_thing, "vol")(fluxy_stuff(sym.int_tpair(nds, "product"))) + #op = (sym.int_tpair(nds, "product").int) + #op = sym.FaceMassOperator()(fluxy_stuff(sym.int_tpair(nds))) + print(op) + + print(sym.pretty(op)) + u = bind(discr, op)(queue, t=0) + + + print(u) + + + + + #vis.write_vtk_file("fld-000o.vtu", [ ("u", o) ]) + #vis.write_vtk_file("fld-000u.vtu", [ ("u", u) ]) + + + + + + + +if __name__ == "__main__": + main(4) + + diff --git a/examples/quad/mass_quad.py b/examples/quad/mass_quad.py new file mode 100644 index 0000000000000000000000000000000000000000..05c306bd9af5bd1838a2b09e325c663163f425b5 --- /dev/null +++ b/examples/quad/mass_quad.py @@ -0,0 +1,90 @@ +# 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 <http://www.gnu.org/licenses/>. + + +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(order=1): + cl_ctx = cl.create_some_context() + queue = cl.CommandQueue(cl_ctx) + + dim = 2 + + + from meshmode.mesh.generation import n_gon, generate_regular_rect_mesh + #mesh = n_gon(3,np.array([0.1,0.2,0.4,0.5,0.6,0.7,0.9])) + mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5), + n=(3, 3), order=7) + + + + + pquad_dd = sym.DOFDesc("vol", "product") + to_pquad = sym.interp("vol", pquad_dd) + + def f(x): + x = x[0] / x[0] + #return sym.MassOperator()(x) + return sym.MassOperator(pquad_dd, "vol")(to_pquad(x)) + #return np.dot(from_pquad_stiffness_t, to_pquad(x)) + #return sym.interp("vol", pquad_dd)(sym.sin(3*x[0])) + + discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order, quad_min_degrees={"product": 2 * order} ) + + + + + + + + ones = bind(discr, sym.nodes(dim)[0] / sym.nodes(dim)[0])(queue, t=0) + u = bind(discr, f(sym.nodes(dim)))(queue, t=0) + + print(np.dot(ones.T, u)) + + + + + #vis.write_vtk_file("fld-000o.vtu", [ ("u", o) ]) + #vis.write_vtk_file("fld-000u.vtu", [ ("u", u) ]) + + + + + + + +if __name__ == "__main__": + main(4) + + diff --git a/examples/quad/quad.py b/examples/quad/quad.py new file mode 100644 index 0000000000000000000000000000000000000000..67c41fa234fe939af00af64c022d3979035f319d --- /dev/null +++ b/examples/quad/quad.py @@ -0,0 +1,80 @@ +# 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 <http://www.gnu.org/licenses/>. + + +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(order=1): + cl_ctx = cl.create_some_context() + queue = cl.CommandQueue(cl_ctx) + + dim = 2 + + + from meshmode.mesh.generation import n_gon, generate_regular_rect_mesh + #mesh = n_gon(3,np.array([0.1,0.2,0.4,0.5,0.6,0.7,0.9])) + mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5), + n=(3, 3), order=7) + + + + + pquad_dd = sym.DOFDesc("vol", "product") + + def f(x): + #return sym.sin(3*x[0]) + return sym.interp("vol", pquad_dd)(sym.sin(3*x[0])) + + discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order, quad_min_degrees={"product": 2 * order} ) + + u = bind(discr, f(sym.nodes(dim)))(queue, t=0) + + + + from meshmode.discretization.visualization import make_visualizer + #vis = make_visualizer(queue, discr.quad_volume_discr['product'], 2*order) + #vis = make_visualizer(queue, discr.volume_discr, order) + + #vis.write_vtk_file("fld-000.vtu", [ ("u", u) ]) + print(u) + + + + + + + +if __name__ == "__main__": + main(4) + + diff --git a/examples/quad/stiffness_quad.py b/examples/quad/stiffness_quad.py new file mode 100644 index 0000000000000000000000000000000000000000..ff334c4ec0073f1640ad936475ae96088797ecc6 --- /dev/null +++ b/examples/quad/stiffness_quad.py @@ -0,0 +1,99 @@ +# 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 <http://www.gnu.org/licenses/>. + + +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(order=1): + cl_ctx = cl.create_some_context() + queue = cl.CommandQueue(cl_ctx) + + dim = 2 + + + from meshmode.mesh.generation import n_gon, generate_regular_rect_mesh + #mesh = n_gon(3,np.array([0.1,0.2,0.4,0.5,0.6,0.7,0.9])) + mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5), + n=(3, 3), order=7) + + + + + pquad_dd = sym.DOFDesc("vol", "product") + to_pquad = sym.interp("vol", pquad_dd) + from_pquad_stiffness_t = sym.stiffness_t(dim, pquad_dd, "vol") + from_pquad_mass = sym.MassOperator() + + def lin(x): + return x[0] + + def non_lin(x): + return x[1] * x[0] * x[0] + 3* x[0] * x[0] - 4 + + def f(x, fun): + #return sym.MassOperator()(x) + #return np.dot(sym.stiffness_t(2), to_pquad(fun(x))) + return np.dot(from_pquad_stiffness_t, fun(to_pquad(x))) + + def reg(x, fun): + #return sym.MassOperator()(x) + #return np.dot(sym.stiffness_t(2), to_pquad(fun(x))) + return np.dot(sym.stiffness_t(2), fun(x)) + + discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order, quad_min_degrees={"product": 2 * order} ) + + + #ones = bind(discr, sym.nodes(dim)[0] / sym.nodes(dim)[0])(queue, t=0) + nds = sym.nodes(dim) + #u = bind(discr, to_pquad(nds))(queue, t=0) + #u = bind(discr, reg(nds, lin))(queue, t=0) + u = bind(discr, f(nds, lin))(queue, t=0) + + print(u) + + + + + #vis.write_vtk_file("fld-000o.vtu", [ ("u", o) ]) + #vis.write_vtk_file("fld-000u.vtu", [ ("u", u) ]) + + + + + + + +if __name__ == "__main__": + main(4) + + diff --git a/grudge/discretization.py b/grudge/discretization.py index 2d15c4a74b9b40618e3d869ae334aae2bfc83df3..5a9cea3e261e39e73ca32c00d3590ebfc5f10460 100644 --- a/grudge/discretization.py +++ b/grudge/discretization.py @@ -82,8 +82,7 @@ class DGDiscretizationWithBoundaries(DiscretizationBase): if dd.is_volume(): if qtag is not sym.QTAG_NONE: - # FIXME - raise NotImplementedError("quadrature") + return self._quad_volume_discr(qtag) return self._volume_discr elif dd.domain_tag is sym.FACE_RESTR_ALL: @@ -150,6 +149,12 @@ class DGDiscretizationWithBoundaries(DiscretizationBase): assert from_dd.quadrature_tag is sym.QTAG_NONE return self._boundary_connection(to_dd.domain_tag) + elif to_dd.is_volume(): + from meshmode.discretization.connection import make_same_mesh_connection + to_discr = self._quad_volume_discr(to_dd.quadrature_tag) + from_discr = self._volume_discr + return make_same_mesh_connection(to_discr, from_discr) + else: raise ValueError("cannot interpolate from volume to: " + str(to_dd)) @@ -160,6 +165,24 @@ class DGDiscretizationWithBoundaries(DiscretizationBase): raise ValueError( "cannot interpolate from interior faces to: " + str(to_dd)) + elif from_dd.domain_tag is sym.FACE_RESTR_ALL: + if to_dd.domain_tag is sym.FACE_RESTR_ALL and qtag is not sym.QTAG_NONE: + from meshmode.discretization.connection import make_face_restriction, FACE_RESTR_ALL + return make_face_restriction( + self._all_faces_discr(), + self.group_factory_for_quadrature_tag(qtag), + FACE_RESTR_ALL, + + # FIXME: This will need to change as soon as we support + # pyramids or other elements with non-identical face + # types. + per_face_groups=False) + + #return self._all_faces_connection(None, qtag) + else: + raise ValueError( + "cannot interpolate from interior faces to: " + + str(to_dd)) elif from_dd.domain_tag is sym.FACE_RESTR_ALL: if to_dd.domain_tag is sym.FACE_RESTR_ALL and to_qtag is sym.QTAG_NONE: @@ -189,7 +212,7 @@ class DGDiscretizationWithBoundaries(DiscretizationBase): QuadratureSimplexGroupFactory if quadrature_tag is not sym.QTAG_NONE: - return QuadratureSimplexGroupFactory(order=self.order) + return QuadratureSimplexGroupFactory(order=self.quad_min_degrees[quadrature_tag]) else: return PolynomialWarpAndBlendGroupFactory(order=self.order) @@ -281,7 +304,7 @@ class DGDiscretizationWithBoundaries(DiscretizationBase): return self._all_faces_volume_connection(quadrature_tag).to_discr @memoize_method - def _all_faces_connection(self, boundary_tag): + def _all_faces_connection(self, boundary_tag, to_qtag=None): """Return a :class:`meshmode.discretization.connection.DiscretizationConnection` that goes from either @@ -299,7 +322,7 @@ class DGDiscretizationWithBoundaries(DiscretizationBase): else: faces_conn = self._boundary_connection(boundary_tag) - return make_face_to_all_faces_embedding(faces_conn, self._all_faces_discr()) + return make_face_to_all_faces_embedding(faces_conn, self._all_faces_discr(to_qtag)) # }}} diff --git a/grudge/execution.py b/grudge/execution.py index e4aebe5ca18b495acf4e608ff9f4cdfd218f57b1..40dcb19a1178239b6b617dceb0b676d3a85fd468 100644 --- a/grudge/execution.py +++ b/grudge/execution.py @@ -198,36 +198,42 @@ class ExecutionMapper(mappers.Evaluator, knl = lp.make_kernel( """{[k,i,j]: 0<=k<nelements and - 0<=i,j<ndiscr_nodes}""", + 0<=i<out_ndiscr_nodes and + 0<=j<in_ndiscr_nodes}""", "result[k,i] = sum(j, mat[i, j] * vec[k, j])", default_offset=lp.auto, name="diff") knl = lp.split_iname(knl, "i", 16, inner_tag="l.0") return lp.tag_inames(knl, dict(k="g.0")) - discr = self.discrwb.discr_from_dd(op.dd_in) - # FIXME: This shouldn't really assume that it's dealing with a volume - # input. What about quadrature? What about boundaries? - result = discr.empty( + in_discr = self.discrwb.discr_from_dd(op.dd_in) + out_discr = self.discrwb.discr_from_dd(op.dd_out) + + result = out_discr.empty( queue=self.queue, dtype=field.dtype, allocator=self.bound_op.allocator) - for grp in discr.groups: - cache_key = "elwise_linear", grp, op, field.dtype - try: - matrix = self.bound_op.operator_data_cache[cache_key] - except KeyError: - matrix = ( + for i in range(len(out_discr.groups)): + in_grp = in_discr.groups[i] + out_grp = out_discr.groups[i] + # FIXME: This cache thing + #cache_key = "elwise_linear", grp, op, field.dtype + #try: + #matrix = self.bound_op.operator_data_cache[cache_key] + #except KeyError: + matrix = ( cl.array.to_device( self.queue, - np.asarray(op.matrix(grp), dtype=field.dtype)) + np.asarray(op.matrix(out_grp, in_grp), dtype=field.dtype)) .with_queue(None)) - self.bound_op.operator_data_cache[cache_key] = matrix + #self.bound_op.operator_data_cache[cache_key] = matrix + + knl()(self.queue, mat=matrix, result=out_grp.view(result), + vec=in_grp.view(field)) + - knl()(self.queue, mat=matrix, result=grp.view(result), - vec=grp.view(field)) return result @@ -365,7 +371,6 @@ class ExecutionMapper(mappers.Evaluator, # This should be unified with map_elementwise_linear, which should # be extended to support batching. - discr = self.discrwb.discr_from_dd(repr_op.dd_in) # FIXME: Enable # assert repr_op.dd_in == repr_op.dd_out @@ -377,7 +382,8 @@ class ExecutionMapper(mappers.Evaluator, """{[imatrix,k,i,j]: 0<=imatrix<nmatrices and 0<=k<nelements and - 0<=i,j<nunit_nodes}""", + 0<=i<o_nunit_nodes and + 0<=j<i_nunit_nodes}""", """ result[imatrix, k, i] = sum( j, diff_mat[imatrix, i, j] * vec[k, j]) @@ -388,26 +394,33 @@ class ExecutionMapper(mappers.Evaluator, return lp.tag_inames(knl, dict(k="g.0")) noperators = len(insn.operators) - result = discr.empty( + + in_discr = self.discrwb.discr_from_dd(repr_op.dd_in) + out_discr = self.discrwb.discr_from_dd(repr_op.dd_out) + + result = out_discr.empty( queue=self.queue, dtype=field.dtype, extra_dims=(noperators,), allocator=self.bound_op.allocator) - for grp in discr.groups: - if grp.nelements == 0: + for i in range(len(out_discr.groups)): + in_grp = in_discr.groups[i] + out_grp = out_discr.groups[i] + + if in_grp.nelements == 0: continue - matrices = repr_op.matrices(grp) + matrices = repr_op.matrices(out_grp, in_grp) # FIXME: Should transfer matrices to device and cache them matrices_ary = np.empty(( - noperators, grp.nunit_nodes, grp.nunit_nodes)) + noperators, out_grp.nunit_nodes, in_grp.nunit_nodes)) for i, op in enumerate(insn.operators): matrices_ary[i] = matrices[op.rst_axis] knl()(self.queue, diff_mat=matrices_ary, - result=grp.view(result), vec=grp.view(field)) + result=out_grp.view(result), vec=in_grp.view(field)) return [(name, result[i]) for i, name in enumerate(insn.names)], [] diff --git a/grudge/models/advection.py b/grudge/models/advection.py index 1c83ed9675d5a45c75b22e3b4d5ca7926acf9133..e0203378214983cceb11ecb46d6230a2dfc7574e 100644 --- a/grudge/models/advection.py +++ b/grudge/models/advection.py @@ -175,7 +175,7 @@ class VariableCoefficientAdvectionOperator(HyperbolicOperator): 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)) + #+ 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)) diff --git a/grudge/symbolic/operators.py b/grudge/symbolic/operators.py index 34ec47aaa556369ecea8e688f81bdcbaa0a6db3f..9115360453fffc498bc7e39de515be7f6d009cf9 100644 --- a/grudge/symbolic/operators.py +++ b/grudge/symbolic/operators.py @@ -97,6 +97,12 @@ class ElementwiseLinearOperator(Operator): class InterpolationOperator(Operator): + #def __init__(self, dd_in, dd_out): + #print("ayy") + #print(dd_in) + #print(dd_out) + + #super().__init__(dd_in, dd_out) mapper_method = intern("map_interpolation") @@ -143,6 +149,8 @@ class DiffOperatorBase(Operator): dd_in = _sym().DD_VOLUME if dd_out is None: dd_out = dd_in.with_qtag(_sym().QTAG_NONE) + if dd_out == "vol": #FIXME I'm pretty sure doing it this way is wrong + dd_out = _sym().DD_VOLUME if dd_out.uses_quadrature(): raise ValueError("differentiation outputs are not on " "quadrature grids") @@ -215,18 +223,41 @@ class RefDiffOperator(RefDiffOperatorBase): mapper_method = intern("map_ref_diff") @staticmethod - def matrices(element_group): - return element_group.diff_matrices() + def matrices(out_element_group, in_element_group): + assert in_element_group == out_element_group + return in_element_group.diff_matrices() class RefStiffnessTOperator(RefDiffOperatorBase): mapper_method = intern("map_ref_stiffness_t") @staticmethod - def matrices(element_group): - assert element_group.is_orthogonal_basis() - mmat = element_group.mass_matrix() - return [dmat.T.dot(mmat.T) for dmat in element_group.diff_matrices()] + def matrices(out_element_group, in_element_group): + #FIXME: Is this right? Do we expose weird bugs if we delete this? + if in_element_group == out_element_group: + assert in_element_group.is_orthogonal_basis() + mmat = in_element_group.mass_matrix() + return [dmat.T.dot(mmat.T) for dmat in in_element_group.diff_matrices()] + + n_quad_nodes = in_element_group.nunit_nodes + n_reg_nodes = out_element_group.nunit_nodes + + from modepy import vandermonde + vand = vandermonde(out_element_group.basis(), out_element_group.unit_nodes) + grad_vand = vandermonde(out_element_group.grad_basis(), in_element_group.unit_nodes) + vand_inv_T = np.linalg.inv(vand).T + + grad_basis = out_element_group.grad_basis() + weights = in_element_group.weights + nodes = in_element_group.unit_nodes + + f_res = [np.zeros((n_reg_nodes, n_quad_nodes)) for d in range(out_element_group.dim)] + for d in range(out_element_group.dim): + for i in range(n_reg_nodes): + for j in range(n_quad_nodes): + f_res[d][i,j] = weights[j] * np.dot(vand_inv_T[i,:], grad_vand[d][j,:]) + return f_res + # }}} @@ -338,9 +369,6 @@ class MassOperatorBase(Operator): if dd_out is None: dd_out = dd_in - if dd_out != dd_in: - raise ValueError("dd_out and dd_in must be identical") - super(MassOperatorBase, self).__init__(dd_in, dd_out) @@ -358,19 +386,42 @@ class RefMassOperatorBase(ElementwiseLinearOperator): class RefMassOperator(RefMassOperatorBase): @staticmethod - def matrix(element_group): - return element_group.mass_matrix() + def matrix(out_element_group, in_element_group): + #FIXME: Is this normal? Will bugs surface if I remove this? + if out_element_group == in_element_group: + return element_group.mass_matrix() + + n_quad_nodes = in_element_group.nunit_nodes + n_reg_nodes = out_element_group.nunit_nodes + + from modepy import vandermonde + vand = vandermonde(out_element_group.basis(), out_element_group.unit_nodes) + o_vand = vandermonde(out_element_group.basis(), in_element_group.unit_nodes) + vand_inv_T = np.linalg.inv(vand).T + + grad_basis = out_element_group.grad_basis() + weights = in_element_group.weights + nodes = in_element_group.unit_nodes + + f_res = np.zeros((n_reg_nodes, n_quad_nodes)) + for i in range(n_reg_nodes): + for j in range(n_quad_nodes): + f_res[i,j] = weights[j] * np.dot(vand_inv_T[i,:], o_vand[j,:]) + + return f_res + mapper_method = intern("map_ref_mass") class RefInverseMassOperator(RefMassOperatorBase): @staticmethod - def matrix(element_group): + def matrix(in_element_group, out_element_group): + assert in_element_group == out_element_group import modepy as mp return mp.inverse_mass_matrix( - element_group.basis(), - element_group.unit_nodes) + in_element_group.basis(), + in_element_group.unit_nodes) mapper_method = intern("map_ref_inverse_mass") @@ -405,7 +456,7 @@ class FaceMassOperatorBase(ElementwiseLinearOperator): if dd_in is None: dd_in = sym.DOFDesc(sym.FACE_RESTR_ALL, None) - if dd_out is None: + if dd_out is None or dd_out == "vol": dd_out = sym.DOFDesc("vol", sym.QTAG_NONE) if dd_out.uses_quadrature(): raise ValueError("face mass operator outputs are not on " @@ -476,10 +527,10 @@ def stiffness(dim): [StiffnessOperator(i) for i in range(dim)]) -def stiffness_t(dim): +def stiffness_t(dim, dd_in=None, dd_out=None): from pytools.obj_array import make_obj_array return make_obj_array( - [StiffnessTOperator(i) for i in range(dim)]) + [StiffnessTOperator(i, dd_in, dd_out) for i in range(dim)]) def integral(arg, dd=None): diff --git a/grudge/symbolic/primitives.py b/grudge/symbolic/primitives.py index d64aef3a39f7927c80ccc85b48b5850d83c03bfb..91122a89e215ba26c6ea22b39f7efd21ea49f47e 100644 --- a/grudge/symbolic/primitives.py +++ b/grudge/symbolic/primitives.py @@ -653,10 +653,15 @@ class TracePair: return 0.5*(self.int + self.ext) -def int_tpair(expression): - i = cse(_sym().interp("vol", "int_faces")(expression)) +def int_tpair(expression, qtag=None): + dd = _sym().DOFDesc("int_faces", qtag) + i = cse(_sym().interp("vol", dd)(expression)) e = cse(_sym().OppositeInteriorFaceSwap()(i)) - return TracePair("int_faces", i, e) + return TracePair(dd, i, e) + + #i = cse(_sym().interp("vol", "int_faces")(expression)) + #e = cse(_sym().OppositeInteriorFaceSwap()(i)) + #return TracePair("int_faces", i, e) def bdry_tpair(dd, interior, exterior):