class DGDiscretization: def __init__(self, actx, mesh, order): self.order = order from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory self.group_factory = PolynomialWarpAndBlendGroupFactory(order=order) self.volume_discr = Discretization(actx, mesh, self.group_factory) assert self.volume_discr.dim == 2 @property def _setup_actx(self): return self.volume_discr._setup_actx @property def array_context(self): return self.volume_discr.array_context @property def dim(self): return self.volume_discr.dim # {{{ discretizations/connections @memoize_method def boundary_connection(self, boundary_tag): from meshmode.discretization.connection import make_face_restriction return make_face_restriction(self.volume_discr._setup_actx, self.volume_discr, self.group_factory, boundary_tag=boundary_tag) @memoize_method def interior_faces_connection(self): from meshmode.discretization.connection import (make_face_restriction, FACE_RESTR_INTERIOR) return make_face_restriction(self.volume_discr._setup_actx, self.volume_discr, self.group_factory, FACE_RESTR_INTERIOR, per_face_groups=False) @memoize_method def opposite_face_connection(self): from meshmode.discretization.connection import \ make_opposite_face_connection return make_opposite_face_connection(self._setup_actx, self.interior_faces_connection()) @memoize_method def all_faces_connection(self): from meshmode.discretization.connection import (make_face_restriction, FACE_RESTR_ALL) return make_face_restriction(self.volume_discr._setup_actx, self.volume_discr, self.group_factory, FACE_RESTR_ALL, per_face_groups=False) @memoize_method def get_to_all_face_embedding(self, where): from meshmode.discretization.connection import \ make_face_to_all_faces_embedding faces_conn = self.get_connection("vol", where) return make_face_to_all_faces_embedding(self._setup_actx, faces_conn, self.get_discr("all_faces")) def get_connection(self, src, tgt): src_tgt = (src, tgt) if src_tgt == ("vol", "int_faces"): return self.interior_faces_connection() elif src_tgt == ("vol", "all_faces"): return self.all_faces_connection() elif src_tgt == ("vol", BTAG_ALL): return self.boundary_connection(tgt) elif src_tgt == ("int_faces", "all_faces"): return self.get_to_all_face_embedding(src) elif src_tgt == (BTAG_ALL, "all_faces"): return self.get_to_all_face_embedding(src) else: raise ValueError(f"locations '{src}'->'{tgt}' not understood") def interp(self, src, tgt, vec): if (isinstance(vec, np.ndarray) and vec.dtype.char == "O" and not isinstance(vec, DOFArray)): return obj_array_vectorize(lambda el: self.interp(src, tgt, el), vec) return self.get_connection(src, tgt)(vec) def get_discr(self, where): if where == "vol": return self.volume_discr elif where == "all_faces": return self.all_faces_connection().to_discr elif where == "int_faces": return self.interior_faces_connection().to_discr elif where == BTAG_ALL: return self.boundary_connection(where).to_discr else: raise ValueError(f"location '{where}' not understood") # }}} @memoize_method def parametrization_derivative(self): return freeze( parametrization_derivative(self._setup_actx, self.volume_discr)) @memoize_method def vol_jacobian(self): [a, b], [c, d] = thaw(self._setup_actx, self.parametrization_derivative()) return freeze(a * d - b * c) @memoize_method def inverse_parametrization_derivative(self): [a, b], [c, d] = thaw(self._setup_actx, self.parametrization_derivative()) result = np.zeros((2, 2), dtype=object) det = a * d - b * c result[0, 0] = d / det result[0, 1] = -b / det result[1, 0] = -c / det result[1, 1] = a / det return freeze(result) def zeros(self, actx): return self.volume_discr.zeros(actx) def grad(self, vec): ipder = self.inverse_parametrization_derivative() dref = [ self.volume_discr.num_reference_derivative((idim, ), vec) for idim in range(self.volume_discr.dim) ] return make_obj_array([ sum(dref_i * ipder_i for dref_i, ipder_i in zip(dref, ipder[iambient])) for iambient in range(self.volume_discr.ambient_dim) ]) def div(self, vecs): return sum(self.grad(vec_i)[i] for i, vec_i in enumerate(vecs)) @memoize_method def normal(self, where): bdry_discr = self.get_discr(where) ((a, ), (b, )) = parametrization_derivative(self._setup_actx, bdry_discr) nrm = 1 / (a**2 + b**2)**0.5 return freeze(flat_obj_array(b * nrm, -a * nrm)) @memoize_method def face_jacobian(self, where): bdry_discr = self.get_discr(where) ((a, ), (b, )) = parametrization_derivative(self._setup_actx, bdry_discr) return freeze((a**2 + b**2)**0.5) @memoize_method def get_inverse_mass_matrix(self, grp, dtype): import modepy as mp matrix = mp.inverse_mass_matrix(grp.basis(), grp.unit_nodes) actx = self._setup_actx return actx.freeze(actx.from_numpy(matrix)) def inverse_mass(self, vec): if (isinstance(vec, np.ndarray) and vec.dtype.char == "O" and not isinstance(vec, DOFArray)): return obj_array_vectorize(lambda el: self.inverse_mass(el), vec) @memoize_in(self, "elwise_linear_knl") def knl(): return make_loopy_program( """{[iel,idof,j]: 0<=iel<nelements and 0<=idof<ndiscr_nodes_out and 0<=j<ndiscr_nodes_in}""", "result[iel,idof] = sum(j, mat[idof, j] * vec[iel, j])", name="diff") discr = self.volume_discr result = discr.empty_like(vec) for grp in discr.groups: matrix = self.get_inverse_mass_matrix(grp, vec.entry_dtype) vec.array_context.call_loopy(knl(), mat=matrix, result=result[grp.index], vec=vec[grp.index]) return result / self.vol_jacobian() @memoize_method def get_local_face_mass_matrix(self, afgrp, volgrp, dtype): nfaces = volgrp.mesh_el_group.nfaces assert afgrp.nelements == nfaces * volgrp.nelements matrix = np.empty((volgrp.nunit_dofs, nfaces, afgrp.nunit_dofs), dtype=dtype) from modepy.tools import UNIT_VERTICES import modepy as mp for iface, fvi in enumerate( volgrp.mesh_el_group.face_vertex_indices()): face_vertices = UNIT_VERTICES[volgrp.dim][np.array(fvi)].T matrix[:, iface, :] = mp.nodal_face_mass_matrix( volgrp.basis(), volgrp.unit_nodes, afgrp.unit_nodes, volgrp.order, face_vertices) actx = self._setup_actx return actx.freeze(actx.from_numpy(matrix)) def face_mass(self, vec): if (isinstance(vec, np.ndarray) and vec.dtype.char == "O" and not isinstance(vec, DOFArray)): return obj_array_vectorize(lambda el: self.face_mass(el), vec) @memoize_in(self, "face_mass_knl") def knl(): return make_loopy_program( """{[iel,idof,f,j]: 0<=iel<nelements and 0<=f<nfaces and 0<=idof<nvol_nodes and 0<=j<nface_nodes}""", "result[iel,idof] = " "sum(f, sum(j, mat[idof, f, j] * vec[f, iel, j]))", name="face_mass") all_faces_conn = self.get_connection("vol", "all_faces") all_faces_discr = all_faces_conn.to_discr vol_discr = all_faces_conn.from_discr result = vol_discr.empty_like(vec) fj = self.face_jacobian("all_faces") vec = vec * fj assert len(all_faces_discr.groups) == len(vol_discr.groups) for afgrp, volgrp in zip(all_faces_discr.groups, vol_discr.groups): nfaces = volgrp.mesh_el_group.nfaces matrix = self.get_local_face_mass_matrix(afgrp, volgrp, vec.entry_dtype) vec.array_context.call_loopy(knl(), mat=matrix, result=result[volgrp.index], vec=vec[afgrp.index].reshape( nfaces, volgrp.nelements, afgrp.nunit_dofs)) return result
class DGDiscretization: def __init__(self, cl_ctx, mesh, order): self.order = order from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory self.group_factory = PolynomialWarpAndBlendGroupFactory(order=order) self.volume_discr = Discretization(cl_ctx, mesh, self.group_factory) assert self.volume_discr.dim == 2 @property def cl_context(self): return self.volume_discr.cl_context @property def dim(self): return self.volume_discr.dim # {{{ discretizations/connections @memoize_method def boundary_connection(self, boundary_tag): from meshmode.discretization.connection import make_face_restriction return make_face_restriction(self.volume_discr, self.group_factory, boundary_tag=boundary_tag) @memoize_method def interior_faces_connection(self): from meshmode.discretization.connection import (make_face_restriction, FACE_RESTR_INTERIOR) return make_face_restriction(self.volume_discr, self.group_factory, FACE_RESTR_INTERIOR, per_face_groups=False) @memoize_method def opposite_face_connection(self): from meshmode.discretization.connection import \ make_opposite_face_connection return make_opposite_face_connection(self.interior_faces_connection()) @memoize_method def all_faces_connection(self): from meshmode.discretization.connection import (make_face_restriction, FACE_RESTR_ALL) return make_face_restriction(self.volume_discr, self.group_factory, FACE_RESTR_ALL, per_face_groups=False) @memoize_method def get_to_all_face_embedding(self, where): from meshmode.discretization.connection import \ make_face_to_all_faces_embedding faces_conn = self.get_connection("vol", where) return make_face_to_all_faces_embedding(faces_conn, self.get_discr("all_faces")) def get_connection(self, src, tgt): src_tgt = (src, tgt) if src_tgt == ("vol", "int_faces"): return self.interior_faces_connection() elif src_tgt == ("vol", "all_faces"): return self.all_faces_connection() elif src_tgt == ("vol", BTAG_ALL): return self.boundary_connection(tgt) elif src_tgt == ("int_faces", "all_faces"): return self.get_to_all_face_embedding(src) elif src_tgt == (BTAG_ALL, "all_faces"): return self.get_to_all_face_embedding(src) else: raise ValueError(f"locations '{src}'->'{tgt}' not understood") def interp(self, src, tgt, vec): if is_obj_array(vec): return with_object_array_or_scalar( lambda el: self.interp(src, tgt, el), vec) return self.get_connection(src, tgt)(vec.queue, vec) def get_discr(self, where): if where == "vol": return self.volume_discr elif where == "all_faces": return self.all_faces_connection().to_discr elif where == "int_faces": return self.interior_faces_connection().to_discr elif where == BTAG_ALL: return self.boundary_connection(where).to_discr else: raise ValueError(f"location '{where}' not understood") # }}} @memoize_method def parametrization_derivative(self): with cl.CommandQueue(self.cl_context) as queue: return without_queue( parametrization_derivative(queue, self.volume_discr)) @memoize_method def vol_jacobian(self): with cl.CommandQueue(self.cl_context) as queue: [a, b], [c, d] = with_queue(queue, self.parametrization_derivative()) return (a * d - b * c).with_queue(None) @memoize_method def inverse_parametrization_derivative(self): with cl.CommandQueue(self.cl_context) as queue: [a, b], [c, d] = with_queue(queue, self.parametrization_derivative()) result = np.zeros((2, 2), dtype=object) det = a * d - b * c result[0, 0] = d / det result[0, 1] = -b / det result[1, 0] = -c / det result[1, 1] = a / det return without_queue(result) def zeros(self, queue): return self.volume_discr.zeros(queue) def grad(self, vec): ipder = self.inverse_parametrization_derivative() queue = vec.queue dref = [ self.volume_discr.num_reference_derivative(queue, (idim, ), vec).with_queue(queue) for idim in range(self.volume_discr.dim) ] return make_obj_array([ sum(dref_i * ipder_i for dref_i, ipder_i in zip(dref, ipder[iambient])) for iambient in range(self.volume_discr.ambient_dim) ]) def div(self, vecs): return sum(self.grad(vec_i)[i] for i, vec_i in enumerate(vecs)) @memoize_method def normal(self, where): bdry_discr = self.get_discr(where) with cl.CommandQueue(self.cl_context) as queue: ((a, ), (b, )) = with_queue(queue, parametrization_derivative(queue, bdry_discr)) nrm = 1 / (a**2 + b**2)**0.5 return without_queue(join_fields(b * nrm, -a * nrm)) @memoize_method def face_jacobian(self, where): bdry_discr = self.get_discr(where) with cl.CommandQueue(self.cl_context) as queue: ((a, ), (b, )) = with_queue(queue, parametrization_derivative(queue, bdry_discr)) return ((a**2 + b**2)**0.5).with_queue(None) @memoize_method def get_inverse_mass_matrix(self, grp, dtype): import modepy as mp matrix = mp.inverse_mass_matrix(grp.basis(), grp.unit_nodes) with cl.CommandQueue(self.cl_context) as queue: return (cla.to_device(queue, matrix).with_queue(None)) def inverse_mass(self, vec): if is_obj_array(vec): return with_object_array_or_scalar( lambda el: self.inverse_mass(el), vec) @memoize_in(self, "elwise_linear_knl") def knl(): knl = lp.make_kernel("""{[k,i,j]: 0<=k<nelements and 0<=i<ndiscr_nodes_out and 0<=j<ndiscr_nodes_in}""", "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.volume_discr result = discr.empty(queue=vec.queue, dtype=vec.dtype) for grp in discr.groups: matrix = self.get_inverse_mass_matrix(grp, vec.dtype) knl()(vec.queue, mat=matrix, result=grp.view(result), vec=grp.view(vec)) return result / self.vol_jacobian() @memoize_method def get_local_face_mass_matrix(self, afgrp, volgrp, dtype): nfaces = volgrp.mesh_el_group.nfaces assert afgrp.nelements == nfaces * volgrp.nelements matrix = np.empty((volgrp.nunit_nodes, nfaces, afgrp.nunit_nodes), dtype=dtype) from modepy.tools import UNIT_VERTICES import modepy as mp for iface, fvi in enumerate( volgrp.mesh_el_group.face_vertex_indices()): face_vertices = UNIT_VERTICES[volgrp.dim][np.array(fvi)].T matrix[:, iface, :] = mp.nodal_face_mass_matrix( volgrp.basis(), volgrp.unit_nodes, afgrp.unit_nodes, volgrp.order, face_vertices) with cl.CommandQueue(self.cl_context) as queue: return (cla.to_device(queue, matrix).with_queue(None)) def face_mass(self, vec): if is_obj_array(vec): return with_object_array_or_scalar(lambda el: self.face_mass(el), vec) @memoize_in(self, "face_mass_knl") def knl(): knl = lp.make_kernel( """{[k,i,f,j]: 0<=k<nelements and 0<=f<nfaces and 0<=i<nvol_nodes and 0<=j<nface_nodes}""", "result[k,i] = sum(f, sum(j, mat[i, f, j] * vec[f, k, j]))", default_offset=lp.auto, name="face_mass") knl = lp.split_iname(knl, "i", 16, inner_tag="l.0") return lp.tag_inames(knl, dict(k="g.0")) all_faces_conn = self.get_connection("vol", "all_faces") all_faces_discr = all_faces_conn.to_discr vol_discr = all_faces_conn.from_discr result = vol_discr.empty(queue=vec.queue, dtype=vec.dtype) fj = self.face_jacobian("all_faces") vec = vec * fj assert len(all_faces_discr.groups) == len(vol_discr.groups) for afgrp, volgrp in zip(all_faces_discr.groups, vol_discr.groups): nfaces = volgrp.mesh_el_group.nfaces matrix = self.get_local_face_mass_matrix(afgrp, volgrp, vec.dtype) input_view = afgrp.view(vec).reshape(nfaces, volgrp.nelements, afgrp.nunit_nodes) knl()(vec.queue, mat=matrix, result=volgrp.view(result), vec=input_view) return result