def rewrite_derivative(ref_class, field, dd_in, with_jacobian=True): jac_tag = sym.area_element(self.ambient_dim, self.dim, dd=dd_in) rec_field = self.rec(field) if with_jacobian: rec_field = jac_tag * rec_field return sum( 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))
def map_operator_binding(self, expr): # Global-to-reference is run after operator specialization, so # if we encounter non-quadrature operators here, we know they # must be nodal. dd_in = expr.op.dd_in dd_out = expr.op.dd_out if dd_in.is_volume(): dim = self.dim else: dim = self.dim - 1 jac_in = sym.area_element(self.ambient_dim, dim, dd=dd_in) jac_noquad = sym.area_element(self.ambient_dim, dim, dd=dd_in.with_discr_tag( dof_desc.DISCR_TAG_BASE)) def rewrite_derivative(ref_class, field, dd_in, with_jacobian=True): def imd(rst): return sym.inverse_surface_metric_derivative( rst, expr.op.xyz_axis, ambient_dim=self.ambient_dim, dim=self.dim, dd=dd_in) rec_field = self.rec(field) if with_jacobian: jac_tag = sym.area_element(self.ambient_dim, self.dim, dd=dd_in) rec_field = jac_tag * rec_field return sum( ref_class(rst_axis, dd_in=dd_in)(rec_field * imd(rst_axis)) for rst_axis in range(self.dim)) else: return sum( ref_class(rst_axis, dd_in=dd_in)(rec_field) * imd(rst_axis) for rst_axis in range(self.dim)) if isinstance(expr.op, op.MassOperator): return op.RefMassOperator(dd_in, dd_out)(jac_in * self.rec(expr.field)) elif isinstance(expr.op, op.InverseMassOperator): # based on https://arxiv.org/pdf/1608.03836.pdf return ( 1.0 / jac_in * op.RefInverseMassOperator(dd_in, dd_out)(self.rec(expr.field))) elif isinstance(expr.op, op.FaceMassOperator): jac_in_surf = sym.area_element(self.ambient_dim, self.dim - 1, dd=dd_in) return op.RefFaceMassOperator(dd_in, dd_out)(jac_in_surf * self.rec(expr.field)) elif isinstance(expr.op, op.StiffnessOperator): return op.RefMassOperator(dd_in=dd_in, dd_out=dd_out)( jac_noquad * self.rec(op.DiffOperator(expr.op.xyz_axis)(expr.field))) elif isinstance(expr.op, op.DiffOperator): return rewrite_derivative(op.RefDiffOperator, expr.field, dd_in=dd_in, with_jacobian=False) elif isinstance(expr.op, op.StiffnessTOperator): return rewrite_derivative(op.RefStiffnessTOperator, expr.field, dd_in=dd_in) elif isinstance(expr.op, op.MInvSTOperator): return self.rec(op.InverseMassOperator()(op.StiffnessTOperator( expr.op.xyz_axis)(self.rec(expr.field)))) else: return IdentityMapper.map_operator_binding(self, expr)
def test_face_normal_surface(actx_factory, mesh_name): """Check that face normals are orthogonal to the surface normal""" actx = actx_factory() # {{{ geometry if mesh_name == "2-1-ellipse": from mesh_data import EllipseMeshBuilder builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0) elif mesh_name == "spheroid": from mesh_data import SpheroidMeshBuilder builder = SpheroidMeshBuilder() else: raise ValueError("unknown mesh name: %s" % mesh_name) mesh = builder.get_mesh(builder.resolutions[0], builder.mesh_order) discr = DiscretizationCollection(actx, mesh, order=builder.order) volume_discr = discr.discr_from_dd(dof_desc.DD_VOLUME) logger.info("ndofs: %d", volume_discr.ndofs) logger.info("nelements: %d", volume_discr.mesh.nelements) # }}} # {{{ symbolic from meshmode.discretization.connection import FACE_RESTR_INTERIOR dv = dof_desc.DD_VOLUME df = dof_desc.as_dofdesc(FACE_RESTR_INTERIOR) ambient_dim = mesh.ambient_dim dim = mesh.dim sym_surf_normal = sym.project(dv, df)(sym.surface_normal(ambient_dim, dim=dim, dd=dv).as_vector()) sym_surf_normal = sym_surf_normal / sym.sqrt(sum(sym_surf_normal**2)) sym_face_normal_i = sym.normal(df, ambient_dim, dim=dim - 1) sym_face_normal_e = sym.OppositeInteriorFaceSwap(df)(sym_face_normal_i) if mesh.ambient_dim == 3: # NOTE: there's only one face tangent in 3d sym_face_tangent = ( sym.pseudoscalar(ambient_dim, dim - 1, dd=df) / sym.area_element(ambient_dim, dim - 1, dd=df)).as_vector() # }}} # {{{ checks def _eval_error(x): return bind(discr, sym.norm(np.inf, sym.var("x", dd=df), dd=df))(actx, x=x) rtol = 1.0e-14 surf_normal = bind(discr, sym_surf_normal)(actx) face_normal_i = bind(discr, sym_face_normal_i)(actx) face_normal_e = bind(discr, sym_face_normal_e)(actx) # check interpolated surface normal is orthogonal to face normal error = _eval_error(surf_normal.dot(face_normal_i)) logger.info("error[n_dot_i]: %.5e", error) assert error < rtol # check angle between two neighboring elements error = _eval_error(face_normal_i.dot(face_normal_e) + 1.0) logger.info("error[i_dot_e]: %.5e", error) assert error > rtol # check orthogonality with face tangent if ambient_dim == 3: face_tangent = bind(discr, sym_face_tangent)(actx) error = _eval_error(face_tangent.dot(face_normal_i)) logger.info("error[t_dot_i]: %.5e", error) assert error < 5 * rtol
def map_operator_binding(self, expr): # Global-to-reference is run after operator specialization, so # if we encounter non-quadrature operators here, we know they # must be nodal. if expr.op.dd_in.is_volume(): dim = self.dim else: dim = self.dim - 1 jac_in = sym.area_element(self.ambient_dim, dim, dd=expr.op.dd_in) jac_noquad = sym.area_element(self.ambient_dim, dim, dd=expr.op.dd_in.with_qtag(sym.QTAG_NONE)) def rewrite_derivative(ref_class, field, dd_in, with_jacobian=True): jac_tag = sym.area_element(self.ambient_dim, self.dim, dd=dd_in) rec_field = self.rec(field) if with_jacobian: rec_field = jac_tag * rec_field return sum( 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)) 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)) elif isinstance(expr.op, op.FaceMassOperator): jac_in_surf = sym.area_element(self.ambient_dim, self.dim - 1, dd=expr.op.dd_in) return op.RefFaceMassOperator(expr.op.dd_in, expr.op.dd_out)( jac_in_surf * self.rec(expr.field)) elif isinstance(expr.op, op.StiffnessOperator): return op.RefMassOperator()( jac_noquad * self.rec( op.DiffOperator(expr.op.xyz_axis)(expr.field))) elif isinstance(expr.op, op.DiffOperator): return rewrite_derivative( op.RefDiffOperator, expr.field, dd_in=expr.op.dd_in, with_jacobian=False) elif isinstance(expr.op, op.StiffnessTOperator): return rewrite_derivative( op.RefStiffnessTOperator, expr.field, dd_in=expr.op.dd_in) elif isinstance(expr.op, op.MInvSTOperator): return self.rec( op.InverseMassOperator()( op.StiffnessTOperator(expr.op.xyz_axis)( self.rec(expr.field)))) else: return IdentityMapper.map_operator_binding(self, expr)