def flux(self): from hedge.flux import FluxVectorPlaceholder, make_normal dim = self.dimensions w = FluxVectorPlaceholder(1+dim) u = w[0] v = w[1:] normal = make_normal(dim) from hedge.tools import join_fields flux_weak = join_fields( np.dot(v.avg, normal), u.avg * normal) if self.flux_type == "central": pass elif self.flux_type == "upwind": # see doc/notes/hedge-notes.tm flux_weak -= self.sign*join_fields( 0.5*(u.int-u.ext), 0.5*(normal * np.dot(normal, v.int-v.ext))) else: raise ValueError("invalid flux type '%s'" % self.flux_type) flux_strong = join_fields( np.dot(v.int, normal), u.int * normal) - flux_weak return -self.c*flux_strong
def flux_num(self, q, fluxes, bdry_tag_state_flux): n = self.len_q d = len(fluxes) fvph = FluxVectorPlaceholder(n*(d+1)+1) speed_ph = fvph[0] state_ph = fvph[1:n+1] fluxes_ph = [fvph[i*n+1:(i+1)*n+1] for i in range(1,d+1)] normal = make_normal(d) flux_strong = 0.5*sum(n_i*(f_i.ext-f_i.int) for n_i, f_i in zip(normal, fluxes_ph)) if self.flux_type == "central": pass elif self.flux_type == "lf": penalty = flux_max(speed_ph.int,speed_ph.ext)*(state_ph.ext-state_ph.int) flux_strong = 0.5 * penalty + flux_strong else: raise ValueError("Invalid flux type '%s'" % self.flux_type) flux_op = get_flux_operator(flux_strong) int_operand = join_fields(self.wave_speed(q), q, *fluxes) return (flux_op(int_operand) +sum(flux_op(BoundaryPair(int_operand, join_fields(0, bdry_state, *bdry_fluxes), tag)) for tag, bdry_state, bdry_fluxes in bdry_tag_state_flux))
def flux(self): from hedge.flux import FluxVectorPlaceholder, make_normal dim = self.dimensions w = FluxVectorPlaceholder(2+dim) c = w[0] u = w[1] v = w[2:] normal = make_normal(dim) from hedge.tools import join_fields flux = self.time_sign*1/2*join_fields( c.ext * np.dot(v.ext, normal) - c.int * np.dot(v.int, normal), normal*(c.ext*u.ext - c.int*u.int)) if self.flux_type == "central": pass elif self.flux_type == "upwind": flux += join_fields( c.ext*u.ext - c.int*u.int, c.ext*normal*np.dot(normal, v.ext) - c.int*normal*np.dot(normal, v.int) ) else: raise ValueError("invalid flux type '%s'" % self.flux_type) return flux
def flux(self): from hedge.flux import (make_normal, FluxVectorPlaceholder, flux_max) from pymbolic.primitives import IfPositive d = self.dimensions w = FluxVectorPlaceholder((1 + d) + 1) u = w[0] v = w[1:d + 1] c = w[1 + d] normal = make_normal(self.dimensions) if self.flux_type == "central": return (u.int * numpy.dot(v.int, normal) + u.ext * numpy.dot(v.ext, normal)) * 0.5 elif self.flux_type == "lf": n_vint = numpy.dot(normal, v.int) n_vext = numpy.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) elif self.flux_type == "upwind": return (IfPositive( numpy.dot(normal, v.avg), numpy.dot(normal, v.int) * u.int, # outflow numpy.dot(normal, v.ext) * u.ext, # inflow )) else: raise ValueError, "invalid flux type"
def flux(self): from hedge.flux import make_normal, FluxScalarPlaceholder u = FluxScalarPlaceholder(0) normal = make_normal(self.dimensions) return u.int * numpy.dot(normal, self.v) - self.weak_flux()
def flux(self): from hedge.flux import ( make_normal, FluxVectorPlaceholder, flux_max) from pymbolic.primitives import IfPositive d = self.dimensions w = FluxVectorPlaceholder((1+d)+1) u = w[0] v = w[1:d+1] c = w[1+d] normal = make_normal(self.dimensions) if self.flux_type == "central": return (u.int*numpy.dot(v.int, normal ) + u.ext*numpy.dot(v.ext, normal)) * 0.5 elif self.flux_type == "lf": n_vint = numpy.dot(normal, v.int) n_vext = numpy.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) elif self.flux_type == "upwind": return ( IfPositive(numpy.dot(normal, v.avg), numpy.dot(normal, v.int) * u.int, # outflow numpy.dot(normal, v.ext) * u.ext, # inflow )) else: raise ValueError, "invalid flux type"
def make_lax_friedrichs_flux(wave_speed, state, fluxes, bdry_tags_states_and_fluxes, strong): from pytools.obj_array import join_fields from hedge.flux import make_normal, FluxVectorPlaceholder, flux_max n = len(state) d = len(fluxes) normal = make_normal(d) fvph = FluxVectorPlaceholder(len(state)*(1+d)+1) wave_speed_ph = fvph[0] state_ph = fvph[1:1+n] fluxes_ph = [fvph[1+i*n:1+(i+1)*n] for i in range(1, d+1)] penalty = flux_max(wave_speed_ph.int,wave_speed_ph.ext)*(state_ph.ext-state_ph.int) if not strong: num_flux = 0.5*(sum(n_i*(f_i.int+f_i.ext) for n_i, f_i in zip(normal, fluxes_ph)) - penalty) else: num_flux = 0.5*(sum(n_i*(f_i.int-f_i.ext) for n_i, f_i in zip(normal, fluxes_ph)) + penalty) from hedge.optemplate import get_flux_operator flux_op = get_flux_operator(num_flux) int_operand = join_fields(wave_speed, state, *fluxes) from hedge.optemplate import BoundaryPair return (flux_op(int_operand) + sum( flux_op(BoundaryPair(int_operand, join_fields(0, bdry_state, *bdry_fluxes), tag)) for tag, bdry_state, bdry_fluxes in bdry_tags_states_and_fluxes))
def flux(self): from hedge.flux import FluxVectorPlaceholder, make_normal dim = self.dimensions w = FluxVectorPlaceholder(2 + dim) c = w[0] u = w[1] v = w[2:] normal = make_normal(dim) from hedge.tools import join_fields flux = self.time_sign * 1 / 2 * join_fields( c.ext * numpy.dot(v.ext, normal) - c.int * numpy.dot( v.int, normal), normal * (c.ext * u.ext - c.int * u.int)) if self.flux_type == "central": pass elif self.flux_type == "upwind": flux += join_fields( c.ext * u.ext - c.int * u.int, c.ext * normal * numpy.dot(normal, v.ext) - c.int * normal * numpy.dot(normal, v.int)) else: raise ValueError("invalid flux type '%s'" % self.flux_type) return flux
def flux(self): from hedge.flux import FluxVectorPlaceholder, make_normal dim = self.dimensions w = FluxVectorPlaceholder(1 + dim) u = w[0] v = w[1:] normal = make_normal(dim) from hedge.tools import join_fields flux_weak = join_fields(numpy.dot(v.avg, normal), u.avg * normal) if self.flux_type == "central": pass elif self.flux_type == "upwind": # see doc/notes/hedge-notes.tm flux_weak -= self.sign * join_fields( 0.5 * (u.int - u.ext), 0.5 * (normal * numpy.dot(normal, v.int - v.ext))) else: raise ValueError("invalid flux type '%s'" % self.flux_type) flux_strong = join_fields(numpy.dot(v.int, normal), u.int * normal) - flux_weak return -self.c * flux_strong
def flux(self, beta, is_dirich): """The template for the numerical flux for variable coefficients. From Noels, Radovitzky 2007 """ from hedge.flux import (make_normal, FluxVectorPlaceholder, FluxConstantPlaceholder) from hedge.tools import join_fields dim = self.dimensions normal = make_normal(self.dimensions) w = FluxVectorPlaceholder(dim*2+9) # u is displacement field, v is its time derivative (velocity) u, v, F = self.split_grad_vars(w) P_int = self.material.stress(F.int, self.dimensions) C_int = self.material.tangent_moduli(F.int, self.dimensions, self.dimensions) # constitutive update for exterior face if is_dirich: P_ext = [0]*9 #[-3*p for p in P_int] C_ext = [0]*81 #C_int else: P_ext = self.material.stress(F.ext, self.dimensions) C_ext = self.material.tangent_moduli(F.ext, self.dimensions, self.dimensions) P_avg = [(P_int[i] + P_ext[i])/2 for i in range(dim*dim)] # 'force' flux v_flux = [0,]*self.dimensions for i in range(self.dimensions): for j in range(self.dimensions): v_flux[i] = v_flux[i] + P_avg[3*i+j]*normal[j] from hedge.flux import make_penalty_term stab_factor = beta * make_penalty_term() C_avg = [stab_factor * (C_int[i] + C_ext[i]) / 2 for i in range(dim*dim*dim*dim)] # stabilization term u_jump = u.ext - u.int for i in range(self.dimensions): for j in range(self.dimensions): for k in range(self.dimensions): for l in range(self.dimensions): v_flux[i] = v_flux[i] - normal[j]* \ C_avg[27*i+9*j+3*k+l]* \ u_jump[k]* \ normal[l] return join_fields( # u needs no flux term 0,0,0, # flux for v v_flux[0], v_flux[1], v_flux[2] )
def op_template(self, w=None): """The full operator template - the high level description of the nonlinear mechanics operator. Combines the relevant operator templates for spatial derivatives, flux, boundary conditions etc. NOTE: Only boundary conditions allowed currently are homogenous dirichlet and neumann, and I'm not sure dirichlet is done properly """ from hedge.optemplate import InverseMassOperator, Field, \ make_vector_field from hedge.tools import join_fields w = self.field_placeholder(w) u,v = self.split_vars(w) from hedge.optemplate import make_nabla nabla = make_nabla(self.dimensions) ij = 0 F = [0,]*9 for i in range(self.dimensions): for j in range(self.dimensions): F[ij] = nabla[j](u[i]) if i == j: F[ij] = F[ij] + 1 ij = ij + 1 w = join_fields(u,v,F) flux_w = w from hedge.optemplate import BoundaryPair, get_flux_operator flux_op = get_flux_operator(self.flux(self.beta, is_dirich=False)) d_flux_op = get_flux_operator(self.flux(self.beta, is_dirich=True)) from hedge.optemplate import make_normal, BoundarizeOperator dir_normal = make_normal(self.dirichlet_tag, self.dimensions) dir_bc = self.dirichlet_bc(w) return self.local_derivatives(w) \ - (flux_op(flux_w) + d_flux_op(BoundaryPair(flux_w, dir_bc, self.dirichlet_tag)) )
def flux(self): from hedge.flux import make_normal, FluxVectorPlaceholder v = FluxVectorPlaceholder(self.arg_count) normal = make_normal(self.dimensions) flux = 0 idx = 0 for i, i_enabled in enumerate(self.subset): if i_enabled and i < self.dimensions: flux += (v.int - v.avg)[idx] * normal[i] idx += 1 return flux
def flux(self): from hedge.flux import make_normal, FluxVectorPlaceholder v = FluxVectorPlaceholder(self.arg_count) normal = make_normal(self.dimensions) flux = 0 idx = 0 for i, i_enabled in enumerate(self.subset): if i_enabled and i < self.dimensions: flux += (v.int-v.avg)[idx]*normal[i] idx += 1 return flux
def flux(self): from hedge.flux import make_normal, FluxVectorPlaceholder normal = make_normal(self.maxwell_op.dimensions) from hedge.tools import join_fields w = FluxVectorPlaceholder(self.component_count) e, h, phi = self.split_ehphi(w) # see hedge/doc/maxima/eclean.mac for derivation strong_flux = 0.5 * self.c * self.chi * join_fields( # flux e normal * (phi.int - phi.ext - numpy.dot(normal, e.int - e.ext)), # flux h len(h) * [0], # flux phi numpy.dot(e.int - e.ext, normal) - (phi.int - phi.ext)) return strong_flux + join_fields(self.maxwell_op.flux(1), 0)
def div(self, tgt, bc_getter, dirichlet_tags, neumann_tags): """ :param bc_getter: a function (tag, volume_expr) -> boundary expr. *volume_expr* will be None to query the Neumann condition. """ from hedge.tools.symbolic import make_common_subexpression as cse from hedge.flux import FluxVectorPlaceholder, make_normal, PenaltyTerm normal = make_normal(tgt.dimensions) n_times = tgt.normal_times_flux v_times = tgt.vec_times if tgt.strong_form: def adjust_flux(f): return n_times(flux_v.int) - f else: def adjust_flux(f): return f dim = tgt.dimensions flux_w = FluxVectorPlaceholder(2*tgt.dimensions) flux_v = flux_w[:dim] pure_diff_v = flux_w[dim:] flux_args = ( list(tgt.int_flux_operand) + list(IPDGDerivativeGenerator()(tgt.int_flux_operand))) stab_term_generator = StabilizationTermGenerator(flux_args) stab_term = (self.stab_coefficient * PenaltyTerm() * stab_term_generator(tgt.int_flux_operand)) 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)) tgt.add_derivative(cse(tgt.operand)) tgt.add_inner_fluxes(adjust_flux(flux), flux_arg_int) self.add_div_bcs(tgt, bc_getter, dirichlet_tags, neumann_tags, stab_term, adjust_flux, flux_v, flux_arg_int, 2*tgt.dimensions)
def weak_flux(self): from hedge.flux import make_normal, FluxScalarPlaceholder from pymbolic.primitives import IfPositive u = FluxScalarPlaceholder(0) normal = make_normal(self.dimensions) if self.flux_type == "central": return u.avg * numpy.dot(normal, self.v) elif self.flux_type == "lf": return u.avg*numpy.dot(normal, self.v) \ + 0.5*la.norm(self.v)*(u.int - u.ext) elif self.flux_type == "upwind": return (numpy.dot(normal, self.v) * IfPositive( numpy.dot(normal, self.v), u.int, # outflow u.ext, # inflow )) else: raise ValueError, "invalid flux type"
def get_advection_flux(self, velocity): from hedge.flux import make_normal, FluxScalarPlaceholder from pymbolic.primitives import IfPositive u = FluxScalarPlaceholder(0) normal = make_normal(self.method.dimensions) if self.flux_type == "central": return u.avg * np.dot(normal, velocity) elif self.flux_type == "lf": return u.avg*np.dot(normal, velocity) \ + 0.5*la.norm(v)*(u.int - u.ext) elif self.flux_type == "upwind": return (np.dot(normal, velocity) * IfPositive( np.dot(normal, velocity), u.int, # outflow u.ext, # inflow )) else: raise ValueError, "invalid flux type"
def get_advection_flux(self, velocity): from hedge.flux import make_normal, FluxScalarPlaceholder from pymbolic.primitives import IfPositive u = FluxScalarPlaceholder(0) normal = make_normal(self.method.dimensions) if self.flux_type == "central": return u.avg*np.dot(normal, velocity) elif self.flux_type == "lf": return u.avg*np.dot(normal, velocity) \ + 0.5*la.norm(v)*(u.int - u.ext) elif self.flux_type == "upwind": return (np.dot(normal, velocity)* IfPositive(np.dot(normal, velocity), u.int, # outflow u.ext, # inflow )) else: raise ValueError, "invalid flux type"
def weak_flux(self): from hedge.flux import make_normal, FluxScalarPlaceholder from pymbolic.primitives import IfPositive u = FluxScalarPlaceholder(0) normal = make_normal(self.dimensions) if self.flux_type == "central": return u.avg*numpy.dot(normal, self.v) elif self.flux_type == "lf": return u.avg*numpy.dot(normal, self.v) \ + 0.5*la.norm(self.v)*(u.int - u.ext) elif self.flux_type == "upwind": return (numpy.dot(normal, self.v)* IfPositive(numpy.dot(normal, self.v), u.int, # outflow u.ext, # inflow )) else: raise ValueError, "invalid flux type"
def flux(self): from hedge.flux import make_normal, FluxVectorPlaceholder normal = make_normal(self.maxwell_op.dimensions) from hedge.tools import join_fields w = FluxVectorPlaceholder(self.component_count) e, h, phi = self.split_ehphi(w) # see hedge/doc/maxima/eclean.mac for derivation strong_flux = 0.5*self.c*self.chi*join_fields( # flux e normal*(phi.int-phi.ext - numpy.dot(normal, e.int-e.ext)), # flux h len(h)*[0], # flux phi numpy.dot(e.int-e.ext, normal)-(phi.int-phi.ext) ) return strong_flux + join_fields(self.maxwell_op.flux(1), 0)
def absorbing_bc(self, w=None): """Construct part of the flux operator template for 1st order absorbing boundary conditions. """ from hedge.optemplate import make_normal absorb_normal = make_normal(self.absorb_tag, self.dimensions) from hedge.optemplate import BoundarizeOperator, Field from hedge.tools import join_fields e, h = self.split_eh(self.field_placeholder(w)) if self.fixed_material: epsilon = self.epsilon mu = self.mu else: epsilon = cse( BoundarizeOperator(self.absorb_tag)(Field("epsilon"))) mu = cse( BoundarizeOperator(self.absorb_tag)(Field("mu"))) absorb_Z = (mu/epsilon)**0.5 absorb_Y = 1/absorb_Z absorb_e = BoundarizeOperator(self.absorb_tag)(e) absorb_h = BoundarizeOperator(self.absorb_tag)(h) bc = join_fields( absorb_e + 1/2*(self.space_cross_h(absorb_normal, self.space_cross_e( absorb_normal, absorb_e)) - absorb_Z*self.space_cross_h(absorb_normal, absorb_h)), absorb_h + 1/2*( self.space_cross_e(absorb_normal, self.space_cross_h( absorb_normal, absorb_h)) + absorb_Y*self.space_cross_e(absorb_normal, absorb_e))) return bc
def make_lax_friedrichs_flux(wave_speed, state, fluxes, bdry_tags_states_and_fluxes, strong): from pytools.obj_array import join_fields from hedge.flux import make_normal, FluxVectorPlaceholder, flux_max n = len(state) d = len(fluxes) normal = make_normal(d) fvph = FluxVectorPlaceholder(len(state) * (1 + d) + 1) wave_speed_ph = fvph[0] state_ph = fvph[1:1 + n] fluxes_ph = [fvph[1 + i * n:1 + (i + 1) * n] for i in range(1, d + 1)] penalty = flux_max(wave_speed_ph.int, wave_speed_ph.ext) * (state_ph.ext - state_ph.int) if not strong: num_flux = 0.5 * (sum(n_i * (f_i.int + f_i.ext) for n_i, f_i in zip(normal, fluxes_ph)) - penalty) else: num_flux = 0.5 * (sum(n_i * (f_i.int - f_i.ext) for n_i, f_i in zip(normal, fluxes_ph)) + penalty) from hedge.optemplate import get_flux_operator flux_op = get_flux_operator(num_flux) int_operand = join_fields(wave_speed, state, *fluxes) from hedge.optemplate import BoundaryPair return (flux_op(int_operand) + sum( flux_op( BoundaryPair(int_operand, join_fields(0, bdry_state, *bdry_fluxes), tag)) for tag, bdry_state, bdry_fluxes in bdry_tags_states_and_fluxes))
def test_interior_fluxes_tri(): """Check triangle surface integrals computed using interior fluxes against their known values. """ from math import pi, sin, cos def round_trip_connect(start, end): for i in range(start, end): yield i, i + 1 yield end, start a = -pi b = pi points = [(a, 0), (b, 0), (a, -1), (b, -1), (a, 1), (b, 1)] import meshpy.triangle as triangle mesh_info = triangle.MeshInfo() mesh_info.set_points(points) mesh_info.set_facets([(0, 1), (1, 3), (3, 2), (2, 0), (0, 4), (4, 5), (1, 5)]) mesh_info.regions.resize(2) mesh_info.regions[0] = [0, -0.5, 1, 0.1] # coordinate # lower element tag # max area mesh_info.regions[1] = [0, 0.5, 2, 0.01] # coordinate # upper element tag # max area generated_mesh = triangle.build(mesh_info, attributes=True, volume_constraints=True) # triangle.write_gnuplot_mesh("mesh.dat", generated_mesh) def element_tagger(el): if generated_mesh.element_attributes[el.id] == 1: return ["upper"] else: return ["lower"] from hedge.mesh import make_conformal_mesh mesh = make_conformal_mesh(generated_mesh.points, generated_mesh.elements) from hedge.discretization.local import TriangleDiscretization from hedge.discretization import ones_on_volume discr = discr_class(mesh, TriangleDiscretization(4), debug=discr_class.noninteractive_debug_flags()) def f_u(x, el): if generated_mesh.element_attributes[el.id] == 1: return cos(x[0] - x[1]) else: return 0 def f_l(x, el): if generated_mesh.element_attributes[el.id] == 0: return sin(x[0] - x[1]) else: return 0 u_l = discr.interpolate_volume_function(f_l) u_u = discr.interpolate_volume_function(f_u) u = u_u + u_u # discr.visualize_vtk("dual.vtk", [("u", u)]) from hedge.flux import make_normal, FluxScalarPlaceholder from hedge.optemplate import Field, get_flux_operator fluxu = FluxScalarPlaceholder() res = discr.compile(get_flux_operator((fluxu.int - fluxu.ext) * make_normal(discr.dimensions)[1]) * Field("u"))(u=u) from hedge.discretization import ones_on_volume ones = ones_on_volume(discr) err = abs(numpy.dot(res, ones)) # print err assert err < 5e-14
def op_template(self): from hedge.optemplate import \ make_vector_field, \ BoundaryPair, \ get_flux_operator, \ make_nabla, \ InverseMassOperator, \ BoundarizeOperator d = self.dimensions w = make_vector_field("w", d + 1) u = w[0] v = w[1:] # boundary conditions ------------------------------------------------- from hedge.tools import join_fields # dirichlet BCs ------------------------------------------------------- from hedge.optemplate import make_normal, Field dir_normal = make_normal(self.dirichlet_tag, d) dir_u = BoundarizeOperator(self.dirichlet_tag) * u dir_v = BoundarizeOperator(self.dirichlet_tag) * v if self.dirichlet_bc_f: # FIXME from warnings import warn warn("Inhomogeneous Dirichlet conditions on the wave equation " "are still having issues.") dir_g = Field("dir_bc_u") dir_bc = join_fields(2 * dir_g - dir_u, dir_v) else: dir_bc = join_fields(-dir_u, dir_v) # neumann BCs --------------------------------------------------------- neu_u = BoundarizeOperator(self.neumann_tag) * u neu_v = BoundarizeOperator(self.neumann_tag) * v neu_bc = join_fields(neu_u, -neu_v) # radiation BCs ------------------------------------------------------- from hedge.optemplate import make_normal rad_normal = make_normal(self.radiation_tag, d) rad_u = BoundarizeOperator(self.radiation_tag) * u rad_v = BoundarizeOperator(self.radiation_tag) * v rad_bc = join_fields( 0.5 * (rad_u - self.sign * numpy.dot(rad_normal, rad_v)), 0.5 * rad_normal * (numpy.dot(rad_normal, rad_v) - self.sign * rad_u)) # entire operator ----------------------------------------------------- nabla = make_nabla(d) flux_op = get_flux_operator(self.flux()) from hedge.tools import join_fields result = ( -join_fields(-self.c * numpy.dot(nabla, v), -self.c * (nabla * u)) + InverseMassOperator() * (flux_op(w) + flux_op(BoundaryPair(w, dir_bc, self.dirichlet_tag)) + flux_op(BoundaryPair(w, neu_bc, self.neumann_tag)) + flux_op( BoundaryPair(w, rad_bc, self.radiation_tag)))) if self.source_f is not None: result[0] += Field("source_u") return result
def test_interior_fluxes_tet(): """Check tetrahedron surface integrals computed using interior fluxes against their known values. """ import meshpy.tet as tet from math import pi, sin, cos mesh_info = tet.MeshInfo() # construct a two-box extrusion of this base base = [(-pi, -pi, 0), (pi, -pi, 0), (pi, pi, 0), (-pi, pi, 0)] # first, the nodes mesh_info.set_points(base + [(x, y, z + pi) for x, y, z in base] + [(x, y, z + pi + 1) for x, y, z in base]) # next, the facets # vertex indices for a box missing the -z face box_without_minus_z = [ [4, 5, 6, 7], [0, 4, 5, 1], [1, 5, 6, 2], [2, 6, 7, 3], [3, 7, 4, 0], ] def add_to_all_vertex_indices(facets, increment): return [[pt + increment for pt in facet] for facet in facets] mesh_info.set_facets( [[0, 1, 2, 3]] # base + box_without_minus_z # first box + add_to_all_vertex_indices(box_without_minus_z, 4) # second box ) # set the volume properties -- this is where the tet size constraints are mesh_info.regions.resize(2) mesh_info.regions[0] = [ 0, 0, pi / 2, # point in volume -> first box 0, # region tag (user-defined number) 0.5, # max tet volume in region ] mesh_info.regions[1] = [ 0, 0, pi + 0.5, # point in volume -> second box 1, # region tag (user-defined number, arbitrary) 0.1, # max tet volume in region ] generated_mesh = tet.build(mesh_info, attributes=True, volume_constraints=True) from hedge.mesh import make_conformal_mesh mesh = make_conformal_mesh(generated_mesh.points, generated_mesh.elements) from hedge.discretization.local import TetrahedronDiscretization from hedge.discretization import ones_on_volume discr = discr_class(mesh, TetrahedronDiscretization(4), debug=discr_class.noninteractive_debug_flags()) def f_u(x, el): if generated_mesh.element_attributes[el.id] == 1: return cos(x[0] - x[1] + x[2]) else: return 0 def f_l(x, el): if generated_mesh.element_attributes[el.id] == 0: return sin(x[0] - x[1] + x[2]) else: return 0 u_l = discr.interpolate_volume_function(f_l) u_u = discr.interpolate_volume_function(f_u) u = u_l + u_u # visualize the produced field #from hedge.visualization import SiloVisualizer #vis = SiloVisualizer(discr) #visf = vis.make_file("sandwich") #vis.add_data(visf, #[("u_l", u_l), ("u_u", u_u)], #expressions=[("u", "u_l+u_u")]) # make sure the surface integral of the difference # between top and bottom is zero from hedge.flux import make_normal, FluxScalarPlaceholder from hedge.optemplate import Field, get_flux_operator fluxu = FluxScalarPlaceholder() res = discr.compile( get_flux_operator( (fluxu.int - fluxu.ext) * make_normal(discr.dimensions)[1]) * Field("u"))(u=u) ones = ones_on_volume(discr) assert abs(numpy.dot(res, ones)) < 5e-14
def flux(self): from hedge.flux import make_normal, FluxScalarPlaceholder u = FluxScalarPlaceholder() normal = make_normal(self.dimensions) return u.int * normal - u.avg * normal
def flux(self, flux_type): """The template for the numerical flux for variable coefficients. :param flux_type: can be in [0,1] for anything between central and upwind, or "lf" for Lax-Friedrichs. As per Hesthaven and Warburton page 433. """ from hedge.flux import (make_normal, FluxVectorPlaceholder, FluxConstantPlaceholder) from hedge.tools import join_fields normal = make_normal(self.dimensions) if self.fixed_material: from hedge.tools import count_subset w = FluxVectorPlaceholder(count_subset(self.get_eh_subset())) e, h = self.split_eh(w) epsilon = FluxConstantPlaceholder(self.epsilon) mu = FluxConstantPlaceholder(self.mu) else: from hedge.tools import count_subset w = FluxVectorPlaceholder(count_subset(self.get_eh_subset()) + 2) epsilon, mu, e, h = self.split_eps_mu_eh(w) Z_int = (mu.int / epsilon.int)**0.5 Y_int = 1 / Z_int Z_ext = (mu.ext / epsilon.ext)**0.5 Y_ext = 1 / Z_ext if flux_type == "lf": if self.fixed_material: max_c = (self.epsilon * self.mu)**(-0.5) else: from hedge.flux import Max c_int = (epsilon.int * mu.int)**(-0.5) c_ext = (epsilon.ext * mu.ext)**(-0.5) max_c = Max(c_int, c_ext) # noqa return join_fields( # flux e, 1 / 2 * ( -self.space_cross_h(normal, h.int - h.ext) # multiplication by epsilon undoes material divisor below #-max_c*(epsilon.int*e.int - epsilon.ext*e.ext) ), # flux h 1 / 2 * ( self.space_cross_e(normal, e.int - e.ext) # multiplication by mu undoes material divisor below #-max_c*(mu.int*h.int - mu.ext*h.ext) )) elif isinstance(flux_type, (int, float)): # see doc/maxima/maxwell.mac return join_fields( # flux e, (-1 / (Z_int + Z_ext) * self.space_cross_h( normal, Z_ext * (h.int - h.ext) - flux_type * self.space_cross_e(normal, e.int - e.ext))), # flux h (1 / (Y_int + Y_ext) * self.space_cross_e( normal, Y_ext * (e.int - e.ext) + flux_type * self.space_cross_h(normal, h.int - h.ext))), ) else: raise ValueError("maxwell: invalid flux_type (%s)" % self.flux_type)
def flux(self, flux_type): """The template for the numerical flux for variable coefficients. :param flux_type: can be in [0,1] for anything between central and upwind, or "lf" for Lax-Friedrichs. As per Hesthaven and Warburton page 433. """ from hedge.flux import (make_normal, FluxVectorPlaceholder, FluxConstantPlaceholder) from hedge.tools import join_fields normal = make_normal(self.dimensions) if self.fixed_material: from hedge.tools import count_subset w = FluxVectorPlaceholder(count_subset(self.get_eh_subset())) e, h = self.split_eh(w) epsilon = FluxConstantPlaceholder(self.epsilon) mu = FluxConstantPlaceholder(self.mu) else: from hedge.tools import count_subset w = FluxVectorPlaceholder(count_subset(self.get_eh_subset())+2) epsilon, mu, e, h = self.split_eps_mu_eh(w) Z_int = (mu.int/epsilon.int)**0.5 Y_int = 1/Z_int Z_ext = (mu.ext/epsilon.ext)**0.5 Y_ext = 1/Z_ext if flux_type == "lf": if self.fixed_material: max_c = (self.epsilon*self.mu)**(-0.5) else: from hedge.flux import Max c_int = (epsilon.int*mu.int)**(-0.5) c_ext = (epsilon.ext*mu.ext)**(-0.5) max_c = Max(c_int, c_ext) # noqa return join_fields( # flux e, 1/2*( -self.space_cross_h(normal, h.int-h.ext) # multiplication by epsilon undoes material divisor below #-max_c*(epsilon.int*e.int - epsilon.ext*e.ext) ), # flux h 1/2*( self.space_cross_e(normal, e.int-e.ext) # multiplication by mu undoes material divisor below #-max_c*(mu.int*h.int - mu.ext*h.ext) )) elif isinstance(flux_type, (int, float)): # see doc/maxima/maxwell.mac return join_fields( # flux e, ( -1/(Z_int+Z_ext)*self.space_cross_h(normal, Z_ext*(h.int-h.ext) - flux_type*self.space_cross_e(normal, e.int-e.ext)) ), # flux h ( 1/(Y_int + Y_ext)*self.space_cross_e(normal, Y_ext*(e.int-e.ext) + flux_type*self.space_cross_h(normal, h.int-h.ext)) ), ) else: raise ValueError("maxwell: invalid flux_type (%s)" % self.flux_type)
def flux(self): from hedge.flux import make_normal, FluxScalarPlaceholder u = FluxScalarPlaceholder() normal = make_normal(self.dimensions) return u.int*normal - u.avg*normal
def op_template(self): from hedge.tools import join_fields from hedge.optemplate import Field, make_vector_field, BoundaryPair, \ BoundarizeOperator, make_normal, get_flux_operator, \ make_nabla, InverseMassOperator w = make_vector_field("w", self.component_count) e, h, phi = self.split_ehphi(w) rho = Field("rho") # local part ---------------------------------------------------------- nabla = make_nabla(self.maxwell_op.dimensions) # in conservation form: u_t + A u_x = 0 # we're describing the A u_x part, the sign gets reversed # below. max_local_op = join_fields(self.maxwell_op.local_derivatives(w), 0) c = self.maxwell_op.c chi = self.chi hyp_local_operator = max_local_op + join_fields( c * chi * (nabla * phi), 0 * h, c * chi * numpy.dot(nabla, e) - c * chi * rho / self.maxwell_op.epsilon # sign gets reversed below, so this is actually # the decay it advertises to be. + self.phi_decay * phi) # BCs ----------------------------------------------------------------- pec_tag = self.maxwell_op.pec_tag pec_e = BoundarizeOperator(pec_tag)(e) pec_h = BoundarizeOperator(pec_tag)(h) pec_phi = BoundarizeOperator(pec_tag)(phi) pec_n = make_normal(pec_tag, self.maxwell_op.dimensions) bc = "prev" print "HYP CLEAN BC", bc if bc == "char": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields( -pec_e + 3 / 2 * pec_n * numpy.dot(pec_n, pec_e) + 1 / 2 * pec_phi * pec_n, pec_h, 1 / 2 * (pec_phi + numpy.dot(pec_n, pec_e))) elif bc == "invent": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields(-pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e), pec_h, pec_phi) elif bc == "munz": # Munz et al pec_bc = join_fields(-pec_e, pec_h, pec_phi - numpy.dot(pec_n, pec_e)) elif bc == "prev": # previous condition pec_bc = join_fields(-pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e), pec_h, -pec_phi) # assemble operator --------------------------------------------------- flux_op = get_flux_operator(self.flux()) return -hyp_local_operator + InverseMassOperator()( flux_op(w) + flux_op(BoundaryPair(w, pec_bc, pec_tag)))
def test_interior_fluxes_tri(): """Check triangle surface integrals computed using interior fluxes against their known values. """ from math import pi, sin, cos def round_trip_connect(start, end): for i in range(start, end): yield i, i + 1 yield end, start a = -pi b = pi points = [(a, 0), (b, 0), (a, -1), (b, -1), (a, 1), (b, 1)] import meshpy.triangle as triangle mesh_info = triangle.MeshInfo() mesh_info.set_points(points) mesh_info.set_facets([(0, 1), (1, 3), (3, 2), (2, 0), (0, 4), (4, 5), (1, 5)]) mesh_info.regions.resize(2) mesh_info.regions[0] = [ 0, -0.5, # coordinate 1, # lower element tag 0.1, # max area ] mesh_info.regions[1] = [ 0, 0.5, # coordinate 2, # upper element tag 0.01, # max area ] generated_mesh = triangle.build(mesh_info, attributes=True, volume_constraints=True) #triangle.write_gnuplot_mesh("mesh.dat", generated_mesh) def element_tagger(el): if generated_mesh.element_attributes[el.id] == 1: return ["upper"] else: return ["lower"] from hedge.mesh import make_conformal_mesh mesh = make_conformal_mesh(generated_mesh.points, generated_mesh.elements) from hedge.discretization.local import TriangleDiscretization from hedge.discretization import ones_on_volume discr = discr_class(mesh, TriangleDiscretization(4), debug=discr_class.noninteractive_debug_flags()) def f_u(x, el): if generated_mesh.element_attributes[el.id] == 1: return cos(x[0] - x[1]) else: return 0 def f_l(x, el): if generated_mesh.element_attributes[el.id] == 0: return sin(x[0] - x[1]) else: return 0 # u_l = discr.interpolate_volume_function(f_l) u_u = discr.interpolate_volume_function(f_u) u = u_u + u_u #discr.visualize_vtk("dual.vtk", [("u", u)]) from hedge.flux import make_normal, FluxScalarPlaceholder from hedge.optemplate import Field, get_flux_operator fluxu = FluxScalarPlaceholder() res = discr.compile( get_flux_operator( (fluxu.int - fluxu.ext) * make_normal(discr.dimensions)[1]) * Field("u"))(u=u) ones = ones_on_volume(discr) err = abs(numpy.dot(res, ones)) #print err assert err < 5e-14
def op_template(self): from hedge.tools import join_fields from hedge.optemplate import Field, make_vector_field, BoundaryPair, \ BoundarizeOperator, make_normal, get_flux_operator, \ make_nabla, InverseMassOperator w = make_vector_field("w", self.component_count) e, h, phi = self.split_ehphi(w) rho = Field("rho") # local part ---------------------------------------------------------- nabla = make_nabla(self.maxwell_op.dimensions) # in conservation form: u_t + A u_x = 0 # we're describing the A u_x part, the sign gets reversed # below. max_local_op = join_fields(self.maxwell_op.local_derivatives(w), 0) c = self.maxwell_op.c chi = self.chi hyp_local_operator = max_local_op + join_fields( c*chi*(nabla*phi), 0*h, c*chi*numpy.dot(nabla, e) - c*chi*rho/self.maxwell_op.epsilon # sign gets reversed below, so this is actually # the decay it advertises to be. + self.phi_decay*phi ) # BCs ----------------------------------------------------------------- pec_tag = self.maxwell_op.pec_tag pec_e = BoundarizeOperator(pec_tag)(e) pec_h = BoundarizeOperator(pec_tag)(h) pec_phi = BoundarizeOperator(pec_tag)(phi) pec_n = make_normal(pec_tag, self.maxwell_op.dimensions) bc = "prev" print "HYP CLEAN BC", bc if bc == "char": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields( -pec_e + 3/2 * pec_n * numpy.dot(pec_n, pec_e) + 1/2 * pec_phi * pec_n , pec_h, 1/2*(pec_phi+numpy.dot(pec_n, pec_e)) ) elif bc == "invent": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields( -pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e), pec_h, pec_phi) elif bc == "munz": # Munz et al pec_bc = join_fields( -pec_e, pec_h, pec_phi-numpy.dot(pec_n, pec_e)) elif bc == "prev": # previous condition pec_bc = join_fields( -pec_e +2*pec_n * numpy.dot(pec_n, pec_e), pec_h, -pec_phi) # assemble operator --------------------------------------------------- flux_op = get_flux_operator(self.flux()) return -hyp_local_operator + InverseMassOperator()( flux_op(w) + flux_op(BoundaryPair(w, pec_bc, pec_tag)))
def op_template(self, with_sensor=False): from hedge.optemplate import \ Field, \ make_vector_field, \ BoundaryPair, \ get_flux_operator, \ make_nabla, \ InverseMassOperator, \ BoundarizeOperator d = self.dimensions w = make_vector_field("w", d + 1) u = w[0] v = w[1:] from hedge.tools import join_fields c = Field("c") flux_w = join_fields(c, w) # {{{ boundary conditions from hedge.flux import make_normal normal = make_normal(d) from hedge.tools import join_fields # Dirichlet dir_c = BoundarizeOperator(self.dirichlet_tag) * c dir_u = BoundarizeOperator(self.dirichlet_tag) * u dir_v = BoundarizeOperator(self.dirichlet_tag) * v dir_bc = join_fields(dir_c, -dir_u, dir_v) # Neumann neu_c = BoundarizeOperator(self.neumann_tag) * c neu_u = BoundarizeOperator(self.neumann_tag) * u neu_v = BoundarizeOperator(self.neumann_tag) * v neu_bc = join_fields(neu_c, neu_u, -neu_v) # Radiation from hedge.optemplate import make_normal rad_normal = make_normal(self.radiation_tag, d) rad_c = BoundarizeOperator(self.radiation_tag) * c rad_u = BoundarizeOperator(self.radiation_tag) * u rad_v = BoundarizeOperator(self.radiation_tag) * v rad_bc = join_fields( rad_c, 0.5 * (rad_u - self.time_sign * numpy.dot(rad_normal, rad_v)), 0.5 * rad_normal * (numpy.dot(rad_normal, rad_v) - self.time_sign * rad_u)) # }}} # {{{ diffusion ------------------------------------------------------- from pytools.obj_array import with_object_array_or_scalar def make_diffusion(arg): 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 hedge.second_order import SecondDerivativeTarget # strong_form here allows the reuse the value of grad u. grad_tgt = SecondDerivativeTarget( self.dimensions, strong_form=True, operand=arg) 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=[]) return div_tgt.minv_all else: return 0 # }}} # entire operator ----------------------------------------------------- nabla = make_nabla(d) flux_op = get_flux_operator(self.flux()) return ( -join_fields( -self.time_sign * c * numpy.dot(nabla, v) - make_diffusion(u), -self.time_sign * c * (nabla * u) - with_object_array_or_scalar(make_diffusion, v)) + InverseMassOperator() * (flux_op(flux_w) + flux_op( BoundaryPair(flux_w, dir_bc, self.dirichlet_tag)) + flux_op( BoundaryPair(flux_w, neu_bc, self.neumann_tag)) + flux_op( BoundaryPair(flux_w, rad_bc, self.radiation_tag))))
def normal_times_flux(self, flux): from hedge.flux import make_normal return self.vec_times(make_normal(self.dimensions), flux)
def test_interior_fluxes_tet(): """Check tetrahedron surface integrals computed using interior fluxes against their known values. """ import meshpy.tet as tet from math import pi, sin, cos mesh_info = tet.MeshInfo() # construct a two-box extrusion of this base base = [(-pi, -pi, 0), (pi, -pi, 0), (pi, pi, 0), (-pi, pi, 0)] # first, the nodes mesh_info.set_points(base + [(x, y, z + pi) for x, y, z in base] + [(x, y, z + pi + 1) for x, y, z in base]) # next, the facets # vertex indices for a box missing the -z face box_without_minus_z = [[4, 5, 6, 7], [0, 4, 5, 1], [1, 5, 6, 2], [2, 6, 7, 3], [3, 7, 4, 0]] def add_to_all_vertex_indices(facets, increment): return [[pt + increment for pt in facet] for facet in facets] mesh_info.set_facets( [[0, 1, 2, 3]] # base + box_without_minus_z # first box + add_to_all_vertex_indices(box_without_minus_z, 4) # second box ) # set the volume properties -- this is where the tet size constraints are mesh_info.regions.resize(2) mesh_info.regions[0] = [ 0, 0, pi / 2, # point in volume -> first box 0, # region tag (user-defined number) 0.5, # max tet volume in region ] mesh_info.regions[1] = [ 0, 0, pi + 0.5, # point in volume -> second box 1, # region tag (user-defined number, arbitrary) 0.1, # max tet volume in region ] generated_mesh = tet.build(mesh_info, attributes=True, volume_constraints=True) from hedge.mesh import make_conformal_mesh mesh = make_conformal_mesh(generated_mesh.points, generated_mesh.elements) from hedge.discretization.local import TetrahedronDiscretization from hedge.discretization import ones_on_volume discr = discr_class(mesh, TetrahedronDiscretization(4), debug=discr_class.noninteractive_debug_flags()) def f_u(x, el): if generated_mesh.element_attributes[el.id] == 1: return cos(x[0] - x[1] + x[2]) else: return 0 def f_l(x, el): if generated_mesh.element_attributes[el.id] == 0: return sin(x[0] - x[1] + x[2]) else: return 0 u_l = discr.interpolate_volume_function(f_l) u_u = discr.interpolate_volume_function(f_u) u = u_l + u_u # visualize the produced field # from hedge.visualization import SiloVisualizer # vis = SiloVisualizer(discr) # visf = vis.make_file("sandwich") # vis.add_data(visf, # [("u_l", u_l), ("u_u", u_u)], # expressions=[("u", "u_l+u_u")]) # make sure the surface integral of the difference # between top and bottom is zero from hedge.flux import make_normal, FluxScalarPlaceholder from hedge.optemplate import Field, get_flux_operator fluxu = FluxScalarPlaceholder() res = discr.compile(get_flux_operator((fluxu.int - fluxu.ext) * make_normal(discr.dimensions)[1]) * Field("u"))(u=u) from hedge.discretization import ones_on_volume ones = ones_on_volume(discr) assert abs(numpy.dot(res, ones)) < 5e-14
def op_template(self, with_sensor=False): from hedge.optemplate import \ Field, \ make_sym_vector, \ BoundaryPair, \ get_flux_operator, \ make_nabla, \ InverseMassOperator, \ BoundarizeOperator d = self.dimensions w = make_sym_vector("w", d+1) u = w[0] v = w[1:] from hedge.tools import join_fields flux_w = join_fields(self.c, w) # {{{ boundary conditions from hedge.tools import join_fields # Dirichlet dir_c = BoundarizeOperator(self.dirichlet_tag) * self.c dir_u = BoundarizeOperator(self.dirichlet_tag) * u dir_v = BoundarizeOperator(self.dirichlet_tag) * v dir_bc = join_fields(dir_c, -dir_u, dir_v) # Neumann neu_c = BoundarizeOperator(self.neumann_tag) * self.c neu_u = BoundarizeOperator(self.neumann_tag) * u neu_v = BoundarizeOperator(self.neumann_tag) * v neu_bc = join_fields(neu_c, neu_u, -neu_v) # Radiation from hedge.optemplate import make_normal rad_normal = make_normal(self.radiation_tag, d) rad_c = BoundarizeOperator(self.radiation_tag) * self.c rad_u = BoundarizeOperator(self.radiation_tag) * u rad_v = BoundarizeOperator(self.radiation_tag) * v rad_bc = join_fields( rad_c, 0.5*(rad_u - self.time_sign*np.dot(rad_normal, rad_v)), 0.5*rad_normal*(np.dot(rad_normal, rad_v) - self.time_sign*rad_u) ) # }}} # {{{ diffusion ------------------------------------------------------- from pytools.obj_array import with_object_array_or_scalar def make_diffusion(arg): 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 hedge.second_order import SecondDerivativeTarget # strong_form here allows the reuse the value of grad u. grad_tgt = SecondDerivativeTarget( self.dimensions, strong_form=True, operand=arg) 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=[]) return div_tgt.minv_all else: return 0 # }}} # entire operator ----------------------------------------------------- nabla = make_nabla(d) flux_op = get_flux_operator(self.flux()) return ( - join_fields( - self.time_sign*self.c*np.dot(nabla, v) - make_diffusion(u) + self.source, -self.time_sign*self.c*(nabla*u) - with_object_array_or_scalar( make_diffusion, v) ) + InverseMassOperator() * ( flux_op(flux_w) + flux_op(BoundaryPair(flux_w, dir_bc, self.dirichlet_tag)) + flux_op(BoundaryPair(flux_w, neu_bc, self.neumann_tag)) + flux_op(BoundaryPair(flux_w, rad_bc, self.radiation_tag)) ))
def test_2d_gauss_theorem(): """Verify Gauss's theorem explicitly on a mesh""" from hedge.mesh.generator import make_disk_mesh from math import sin, cos, sqrt, exp, pi from numpy import dot mesh = make_disk_mesh() order = 2 discr = discr_class(mesh, order=order, debug=discr_class.noninteractive_debug_flags()) ref_discr = discr_class(mesh, order=order) from hedge.flux import make_normal, FluxScalarPlaceholder normal = make_normal(discr.dimensions) flux_f_ph = FluxScalarPlaceholder(0) one_sided_x = flux_f_ph.int * normal[0] one_sided_y = flux_f_ph.int * normal[1] def f1(x, el): return sin(3 * x[0]) + cos(3 * x[1]) def f2(x, el): return sin(2 * x[0]) + cos(x[1]) from hedge.discretization import ones_on_volume ones = ones_on_volume(discr) f1_v = discr.interpolate_volume_function(f1) f2_v = discr.interpolate_volume_function(f2) from hedge.optemplate import BoundaryPair, Field, make_nabla, get_flux_operator nabla = make_nabla(discr.dimensions) diff_optp = nabla[0] * Field("f1") + nabla[1] * Field("f2") divergence = nabla[0].apply(discr, f1_v) + nabla[1].apply(discr, f2_v) int_div = discr.integral(divergence) flux_optp = get_flux_operator(one_sided_x)(BoundaryPair(Field("f1"), Field("fz"))) + get_flux_operator(one_sided_y)( BoundaryPair(Field("f2"), Field("fz")) ) from hedge.mesh import TAG_ALL bdry_val = discr.compile(flux_optp)(f1=f1_v, f2=f2_v, fz=discr.boundary_zeros(TAG_ALL)) ref_bdry_val = ref_discr.compile(flux_optp)(f1=f1_v, f2=f2_v, fz=discr.boundary_zeros(TAG_ALL)) boundary_int = dot(bdry_val, ones) if False: from hedge.visualization import SiloVisualizer vis = SiloVisualizer(discr) visf = vis.make_file("test") from hedge.tools import make_obj_array from hedge.mesh import TAG_ALL vis.add_data( visf, [ ("bdry", bdry_val), ("ref_bdry", ref_bdry_val), ("div", divergence), ("f", make_obj_array([f1_v, f2_v])), ("n", discr.volumize_boundary_field(discr.boundary_normals(TAG_ALL), TAG_ALL)), ], expressions=[("bdiff", "bdry-ref_bdry")], ) # print abs(boundary_int-int_div) assert abs(boundary_int - int_div) < 5e-15
def test_2d_gauss_theorem(): """Verify Gauss's theorem explicitly on a mesh""" from hedge.mesh.generator import make_disk_mesh from math import sin, cos from numpy import dot mesh = make_disk_mesh() order = 2 discr = discr_class(mesh, order=order, debug=discr_class.noninteractive_debug_flags()) ref_discr = discr_class(mesh, order=order) from hedge.flux import make_normal, FluxScalarPlaceholder normal = make_normal(discr.dimensions) flux_f_ph = FluxScalarPlaceholder(0) one_sided_x = flux_f_ph.int * normal[0] one_sided_y = flux_f_ph.int * normal[1] def f1(x, el): return sin(3 * x[0]) + cos(3 * x[1]) def f2(x, el): return sin(2 * x[0]) + cos(x[1]) from hedge.discretization import ones_on_volume ones = ones_on_volume(discr) f1_v = discr.interpolate_volume_function(f1) f2_v = discr.interpolate_volume_function(f2) from hedge.optemplate import BoundaryPair, Field, make_nabla, \ get_flux_operator nabla = make_nabla(discr.dimensions) divergence = nabla[0].apply(discr, f1_v) + nabla[1].apply(discr, f2_v) int_div = discr.integral(divergence) flux_optp = ( get_flux_operator(one_sided_x)(BoundaryPair(Field("f1"), Field("fz"))) + get_flux_operator(one_sided_y)(BoundaryPair(Field("f2"), Field("fz")))) from hedge.mesh import TAG_ALL bdry_val = discr.compile(flux_optp)(f1=f1_v, f2=f2_v, fz=discr.boundary_zeros(TAG_ALL)) ref_bdry_val = ref_discr.compile(flux_optp)( f1=f1_v, f2=f2_v, fz=discr.boundary_zeros(TAG_ALL)) boundary_int = dot(bdry_val, ones) if False: from hedge.visualization import SiloVisualizer vis = SiloVisualizer(discr) visf = vis.make_file("test") from hedge.tools import make_obj_array from hedge.mesh import TAG_ALL vis.add_data(visf, [ ("bdry", bdry_val), ("ref_bdry", ref_bdry_val), ("div", divergence), ("f", make_obj_array([f1_v, f2_v])), ("n", discr.volumize_boundary_field(discr.boundary_normals(TAG_ALL), TAG_ALL)), ], expressions=[("bdiff", "bdry-ref_bdry")]) #print abs(boundary_int-int_div) assert abs(boundary_int - int_div) < 5e-15