def op_template_struct(self, u=None): from hedge.optemplate import Field if u is None: u = Field("u") result = DecayFitDiscontinuitySensorBase\ .op_template_struct(self, u) from pymbolic.primitives import IfPositive from hedge.optemplate.primitives import ( CFunction, ScalarParameter) from math import pi from hedge.tools.symbolic import make_common_subexpression as cse if self.correct_for_fit_error: decay_expt = cse(result.decay_expt_corrected, "decay_expt") else: decay_expt = cse(result.decay_expt, "decay_expt") def flat_end_sin(x): return IfPositive(-pi/2-x, -1, IfPositive(x-pi/2, 1, sin(x))) sin = CFunction("sin") isnan = CFunction("isnan") c_abs = CFunction("abs") visc_scale = Field("viscosity_scaling") result.sensor = IfPositive(c_abs(isnan(visc_scale)), ScalarParameter("max_viscosity_scaling"), 0.5*visc_scale * (1+flat_end_sin((decay_expt+2)*pi/2))) return result
def get_mu(self, q, to_quad_op): """ :param to_quad_op: If not *None*, represents an operator which transforms nodal values onto a quadrature grid on which the returned :math:`\mu` needs to be represented. In that case, *q* is assumed to already be on the same quadrature grid. """ if to_quad_op is None: def to_quad_op(x): return x if self.mu == "sutherland": # Sutherland's law: !!!not tested!!! t_s = 110.4 mu_inf = 1.735e-5 result = cse( mu_inf * self.cse_temperature(q) ** 1.5 * (1 + t_s) / (self.cse_temperature(q) + t_s), "sutherland_mu") else: result = self.mu if self.artificial_viscosity_mode == "cns": mapped_sensor = self.sensor() else: mapped_sensor = None if mapped_sensor is not None: result = result + cse(to_quad_op(mapped_sensor), "quad_sensor") return cse(result, "mu")
def make_flux_bc_vector(tag, bc): if self.fixed_material: return bc else: from hedge.optemplate import BoundarizeOperator return join_fields( cse(BoundarizeOperator(tag)(epsilon)), cse(BoundarizeOperator(tag)(mu)), bc)
def collision_update(self, f_bar): from hedge.tools.symbolic import make_common_subexpression as cse rho = cse(self.rho(f_bar), "rho") rho_u = self.rho_u(f_bar) u = cse(rho_u/rho, "u") f_eq_func = self.method.f_equilibrium f_eq = make_obj_array([ f_eq_func(rho, alpha, u) for alpha in range(len(self.method))]) return f_bar - 1/(self.tau+1/2)*(f_bar - f_eq)
def characteristic_velocity_optemplate(self, state): from hedge.optemplate.operators import ElementwiseMaxOperator from hedge.optemplate.primitives import CFunction sqrt = CFunction("sqrt") sound_speed = cse(sqrt( self.equation_of_state.gamma*self.cse_p(state)/self.cse_rho(state)), "sound_speed") u = self.cse_u(state) speed = cse(sqrt(numpy.dot(u, u)), "norm_u") + sound_speed return ElementwiseMaxOperator()(speed)
def inflow_state_inner(self, normal, bc, name): # see hedge/doc/maxima/euler.mac return join_fields( # bc rho cse(bc.rho0 + numpy.dot(normal, bc.dumvec)*bc.rho0/(2*bc.c0) + bc.dpm/(2*bc.c0*bc.c0), "bc_rho_"+name), # bc p cse(bc.p0 + bc.c0*bc.rho0*numpy.dot(normal, bc.dumvec)/2 + bc.dpm/2, "bc_p_"+name), # bc u cse(bc.u0 + normal*numpy.dot(normal, bc.dumvec)/2 + bc.dpm*normal/(2*bc.c0*bc.rho0), "bc_u_"+name))
def primitive_to_conservative(self, prims, use_cses=True): if not use_cses: from hedge.tools.symbolic import make_common_subexpression as cse else: def cse(x, name): return x rho = prims[0] p = prims[1] u = prims[2:] e = self.equation_of_state.p_to_e(p, rho, u) return join_fields( rho, cse(e, "e"), cse(rho * u, "rho_u"))
def ic_expr(t, x, fields): from hedge.optemplate import CFunction from pymbolic.primitives import IfPositive from pytools.obj_array import make_obj_array tanh = CFunction("tanh") sin = CFunction("sin") rho = 1 u0 = 0.05 w = 0.05 delta = 0.05 from hedge.tools.symbolic import make_common_subexpression as cse u = cse(make_obj_array([ IfPositive(x[1]-1/2, u0*tanh(4*(3/4-x[1])/w), u0*tanh(4*(x[1]-1/4)/w)), u0*delta*sin(2*np.pi*(x[0]+1/4))]), "u") return make_obj_array([ op.method.f_equilibrium(rho, alpha, u) for alpha in range(len(op.method)) ])
def grad_interior_flux(self, tgt, u): from hedge.tools.symbolic import make_common_subexpression as cse n_times = tgt.normal_times_flux v_times = tgt.vec_times return n_times( cse(u.avg, "u_avg") - v_times(self.beta(tgt), n_times(u.int-u.ext)))
def make_bc_info(self, bc_name, tag, state, state0=None): """ :param state0: The boundary 'free-stream' state around which the BC is linearized. """ if state0 is None: state0 = make_vector_field(bc_name, self.dimensions+2) state0 = cse(to_bdry_quad(state0)) rho0 = self.rho(state0) p0 = self.cse_p(state0) u0 = self.cse_u(state0) c0 = (self.equation_of_state.gamma * p0 / rho0)**0.5 from hedge.optemplate import BoundarizeOperator bdrize_op = BoundarizeOperator(tag) class SingleBCInfo(Record): pass return SingleBCInfo( rho0=rho0, p0=p0, u0=u0, c0=c0, # notation: suffix "m" for "minus", i.e. "interior" drhom=cse(self.rho(cse(to_bdry_quad(bdrize_op(state)))) - rho0, "drhom"), dumvec=cse(self.cse_u(cse(to_bdry_quad(bdrize_op(state)))) - u0, "dumvec"), dpm=cse(self.cse_p(cse(to_bdry_quad(bdrize_op(state)))) - p0, "dpm"))
def flux(self, q): from pytools import delta return [ # one entry for each flux direction cse(join_fields( # flux rho self.rho_u(q)[i], # flux E cse(self.e(q)+self.cse_p(q))*self.cse_u(q)[i], # flux rho_u make_obj_array([ self.rho_u(q)[i]*self.cse_u(q)[j] + delta(i,j) * self.cse_p(q) for j in range(self.dimensions) ]) ), "%s_flux" % AXES[i]) for i in range(self.dimensions)]
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 outflow_state(self, state): from hedge.optemplate import make_normal normal = make_normal(self.outflow_tag, self.dimensions) bc = self.make_bc_info("bc_q_out", self.outflow_tag, state) # see hedge/doc/maxima/euler.mac return join_fields( # bc rho cse(bc.rho0 + bc.drhom + numpy.dot(normal, bc.dumvec)*bc.rho0/(2*bc.c0) - bc.dpm/(2*bc.c0*bc.c0), "bc_rho_outflow"), # bc p cse(bc.p0 + bc.c0*bc.rho0*numpy.dot(normal, bc.dumvec)/2 + bc.dpm/2, "bc_p_outflow"), # bc u cse(bc.u0 + bc.dumvec - normal*numpy.dot(normal, bc.dumvec)/2 + bc.dpm*normal/(2*bc.c0*bc.rho0), "bc_u_outflow"))
def subst_func(expr): from pymbolic.primitives import Subscript, Variable if isinstance(expr, Subscript): assert (isinstance(expr.aggregate, Variable) and expr.aggregate.name == "q") return cbstate[expr.index] elif isinstance(expr, Variable) and expr.name =="sensor": from hedge.optemplate import BoundarizeOperator result = BoundarizeOperator(tag)(self.sensor()) return cse(to_bdry_quad(result), "bdry_sensor")
def tau(self, to_quad_op, state, mu=None): faceq_state = self.faceq_state() dimensions = self.dimensions # {{{ compute gradient of u --------------------------------------- # Use the product rule to compute the gradient of # u from the gradient of (rho u). This ensures we don't # compute the derivatives twice. from pytools.obj_array import with_object_array_or_scalar dq = with_object_array_or_scalar( to_quad_op, self.grad_of_state()) q = cse(to_quad_op(state)) du = numpy.zeros((dimensions, dimensions), dtype=object) for i in range(dimensions): for j in range(dimensions): du[i,j] = cse( (dq[i+2,j] - self.cse_u(q)[i] * dq[0,j]) / self.rho(q), "du%d_d%s" % (i, AXES[j])) # }}} # {{{ put together viscous stress tau ----------------------------- from pytools import delta if mu is None: mu = self.get_mu(q, to_quad_op) tau = numpy.zeros((dimensions, dimensions), dtype=object) for i in range(dimensions): for j in range(dimensions): tau[i,j] = cse(mu * cse(du[i,j] + du[j,i] - 2/self.dimensions * delta(i,j) * numpy.trace(du)), "tau_%d%d" % (i, j)) return tau
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 op_template(self, u=None): from pymbolic.primitives import IfPositive, Variable from hedge.optemplate.primitives import Field, ScalarParameter from hedge.tools.symbolic import make_common_subexpression as cse from math import pi if u is None: u = Field("u") from hedge.optemplate.operators import ( MassOperator, FilterOperator, OnesOperator) mode_truncator = FilterOperator( persson_peraire_filter_response_function) truncated_u = mode_truncator(u) diff = u - truncated_u el_norm_squared_mass_diff_u = OnesOperator()(MassOperator()(diff)*diff) el_norm_squared_mass_u = OnesOperator()(MassOperator()(u)*u) capital_s_e = cse(el_norm_squared_mass_diff_u / el_norm_squared_mass_u, "S_e") sin = Variable("sin") log10 = Variable("log10") s_e = cse(log10(capital_s_e), "s_e") kappa = ScalarParameter("kappa") eps0 = ScalarParameter("eps0") s_0 = ScalarParameter("s_0") return IfPositive(s_0-self.kappa-s_e, 0, IfPositive(s_e-self.kappa-s_0, eps0, eps0/2*(1+sin(pi*(s_e-s_0)/self.kappa))))
def dirichlet_bc(self, w=None): """ Flux term for dirichlet (displacement) boundary conditions """ u, v = self.split_vars(self.field_placeholder(w)) if self.dirichlet_bc_data is not None: from hedge.optemplate import make_vector_field dir_field = cse( -make_vector_field("dirichlet_bc", 3)) else: dir_field = make_obj_array([0,]*3) from hedge.tools import join_fields return join_fields(dir_field, [0]*3, [0,]*9)
def test_quadrature_tri_mass_mat_monomial(): """Check that quadrature integration on triangles is exact as designed.""" from hedge.mesh.generator import make_square_mesh from math import sqrt, pi, cos, sin mesh = make_square_mesh(a=-1, b=1, max_area=4 * 1 / 8 + 0.001) order = 4 discr = discr_class( mesh, order=order, debug=discr_class.noninteractive_debug_flags(), quad_min_degrees={"quad": 3 * order} ) m, n = 2, 1 f = Monomial((m, n)) f_vec = discr.interpolate_volume_function(lambda x, el: f(x)) # ones = discr.interpolate_volume_function(lambda x, el: 1) int_proj = discr._integral_projection() if False: from hedge.visualization import SiloVisualizer vis = SiloVisualizer(discr) visf = vis.make_file("test") vis.add_data(visf, [("f", f_vec * f_vec)]) visf.close() from hedge.optemplate import MassOperator, Field, QuadratureGridUpsampler f_fld = Field("f") mass_op = discr.compile(MassOperator()(f_fld * f_fld)) from hedge.tools.symbolic import make_common_subexpression as cse f_upsamp = cse(QuadratureGridUpsampler("quad")(f_fld)) quad_mass_op = discr.compile(MassOperator()(f_upsamp * f_upsamp)) num_integral_1 = numpy.dot(int_proj, mass_op(f=f_vec)) num_integral_2 = numpy.dot(int_proj, quad_mass_op(f=f_vec)) true_integral = 4 / ((2 * m + 1) * (2 * n + 1)) err_1 = abs(num_integral_1 - true_integral) err_2 = abs(num_integral_2 - true_integral) print num_integral_1, num_integral_2, true_integral print err_1, err_2 assert err_1 > 1e-8 assert err_2 < 1e-14
def incident_bc(self, w=None): "Flux terms for incident boundary conditions" # NOTE: Untested for inhomogeneous materials, but would usually be # physically meaningless anyway (are there exceptions to this?) e, h = self.split_eh(self.field_placeholder(w)) if not self.fixed_material: from warnings import warn if self.incident_tag != hedge.mesh.TAG_NONE: warn("Incident boundary conditions assume homogeneous"+ " background material, results may be unphysical") from hedge.tools import count_subset from hedge.tools import join_fields fld_cnt = count_subset(self.get_eh_subset()) if self.incident_bc_data is not None: from hedge.optemplate import make_vector_field inc_field = cse( -make_vector_field("incident_bc", fld_cnt)) else: inc_field = make_obj_array([0]*fld_cnt) return inc_field
def grad_of_state_func(self, func, of_what_descr): return cse(self.grad_of( func(self.volq_state()), func(self.faceq_state())), "grad_"+of_what_descr)
def cse_temperature(self, q): return cse(self.temperature(q), "temperature")
def faceq_state(self): return cse(to_int_face_quad(self.state()), "face_quad_state")
def volq_state(self): return cse(to_vol_quad(self.state()), "vol_quad_state")
def cse_rho_u(self, q): return cse(self.rho_u(q), "rho_u")
def cse_p(self, q): return cse(self.p(q), "p")
def cse_u(self, q): return cse(self.u(q), "u")
def op_template(self, with_sensor=False): # {{{ operator preliminaries ------------------------------------------ from hedge.optemplate import (Field, BoundaryPair, get_flux_operator, make_stiffness_t, InverseMassOperator, make_vector_field, ElementwiseMaxOperator, BoundarizeOperator) from hedge.tools.symbolic import make_common_subexpression as cse from hedge.optemplate.operators import ( QuadratureGridUpsampler, QuadratureInteriorFacesGridUpsampler) to_quad = QuadratureGridUpsampler("quad") to_if_quad = QuadratureInteriorFacesGridUpsampler("quad") from hedge.tools import join_fields, \ ptwise_dot u = Field("u") v = make_vector_field("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 hedge.mesh import TAG_ALL bc_c = to_quad(BoundarizeOperator(TAG_ALL)(c)) bc_u = to_quad(Field("bc_u")) bc_v = to_quad(BoundarizeOperator(TAG_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 hedge.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(numpy.dot(minv_st, cse(quad_v*quad_u)) - (flux_op(quad_face_w) + flux_op(BoundaryPair(quad_face_w, bc_w, TAG_ALL)))) \ + diffusion_part
def op_template(self, sensor_scaling=None, viscosity_only=False): u = self.cse_u rho = self.cse_rho rho_u = self.rho_u p = self.p e = self.e # {{{ artificial diffusion def make_artificial_diffusion(): if self.artificial_viscosity_mode not in ["diffusion"]: return 0 dq = self.grad_of_state() return make_obj_array([ self.div( to_vol_quad(self.sensor())*to_vol_quad(dq[i]), to_int_face_quad(self.sensor())*to_int_face_quad(dq[i])) for i in range(dq.shape[0])]) # }}} # {{{ state setup volq_flux = self.flux(self.volq_state()) faceq_flux = self.flux(self.faceq_state()) from hedge.optemplate.primitives import CFunction sqrt = CFunction("sqrt") speed = self.characteristic_velocity_optemplate(self.state()) has_viscosity = not is_zero(self.get_mu(self.state(), to_quad_op=None)) # }}} # {{{ operator assembly ----------------------------------------------- from hedge.flux.tools import make_lax_friedrichs_flux from hedge.optemplate.operators import InverseMassOperator from hedge.optemplate.tools import make_stiffness_t primitive_bcs_as_quad_conservative = dict( (tag, self.primitive_to_conservative(to_bdry_quad(bc))) for tag, bc in self.get_primitive_boundary_conditions().iteritems()) def get_bc_tuple(tag): state = self.state() bc = make_obj_array([ self.get_boundary_condition_for(tag, s_i) for s_i in state]) return tag, bc, self.flux(bc) first_order_part = InverseMassOperator()( numpy.dot(make_stiffness_t(self.dimensions), volq_flux) - make_lax_friedrichs_flux( wave_speed=cse(to_int_face_quad(speed), "emax_c"), state=self.faceq_state(), fluxes=faceq_flux, bdry_tags_states_and_fluxes=[ get_bc_tuple(tag) for tag in self.get_boundary_tags()], strong=False)) if viscosity_only: first_order_part = 0*first_order_part result = join_fields( first_order_part + self.make_second_order_part() + make_artificial_diffusion() + self.make_extra_terms(), speed) if self.source is not None: result = result + join_fields( make_vector_field("source_vect", len(self.state())), # extra field for speed 0) return result
def cse_rho(self, q): return cse(self.rho(q), "rho")