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 add_div_bcs(self, tgt, bc_getter, dirichlet_tags, neumann_tags, stab_term, adjust_flux, flux_v, flux_arg_int, grad_flux_arg_count): from pytools.obj_array import make_obj_array, join_fields n_times = tgt.normal_times_flux def unwrap_cse(expr): from pymbolic.primitives import CommonSubexpression if isinstance(expr, CommonSubexpression): return expr.child else: return expr for tag in dirichlet_tags: dir_bc_w = join_fields([0] * grad_flux_arg_count, [ bc_getter(tag, unwrap_cse(vol_expr)) for vol_expr in flux_arg_int[grad_flux_arg_count:] ]) tgt.add_boundary_flux(adjust_flux(n_times(flux_v.int - stab_term)), flux_arg_int, dir_bc_w, tag) loc_bc_vec = make_obj_array([0] * len(flux_arg_int)) for tag in neumann_tags: neu_bc_w = join_fields( NeumannBCGenerator(tag, bc_getter(tag, None))(tgt.operand), [0] * len(flux_arg_int)) tgt.add_boundary_flux(adjust_flux(n_times(flux_v.ext)), loc_bc_vec, neu_bc_w, tag)
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 add_div_bcs(self, tgt, bc_getter, dirichlet_tags, neumann_tags, stab_term, adjust_flux, flux_v, flux_arg_int, grad_flux_arg_count): from pytools.obj_array import make_obj_array, join_fields n_times = tgt.normal_times_flux def unwrap_cse(expr): from pymbolic.primitives import CommonSubexpression if isinstance(expr, CommonSubexpression): return expr.child else: return expr for tag in dirichlet_tags: dir_bc_w = join_fields( [0]*grad_flux_arg_count, [bc_getter(tag, unwrap_cse(vol_expr)) for vol_expr in flux_arg_int[grad_flux_arg_count:]]) tgt.add_boundary_flux( adjust_flux(n_times(flux_v.int-stab_term)), flux_arg_int, dir_bc_w, tag) loc_bc_vec = make_obj_array([0]*len(flux_arg_int)) for tag in neumann_tags: neu_bc_w = join_fields( NeumannBCGenerator(tag, bc_getter(tag, None))(tgt.operand), [0]*len(flux_arg_int)) tgt.add_boundary_flux( adjust_flux(n_times(flux_v.ext)), loc_bc_vec, neu_bc_w, tag)
def op_template(self): dim = self.dimensions q = make_vector_field('q', self.len_q) f2 = make_vector_field('f2', self.len_f2) w = join_fields(q, f2) dim_subset = (True,) * dim + (False,) * (3-dim) def pad_vec(v, subset): result = numpy.zeros((3,), dtype=object) result[numpy.array(subset, dtype=bool)] = v return result sigma = pad_vec(make_vector_field('sigma', dim),dim_subset) alpha = pad_vec(make_vector_field('alpha', dim),dim_subset) kappa = pad_vec(make_vector_field('kappa', dim),dim_subset) fluxes = self.flux(w, kappa) # Boundary conditions q_bc_stressfree = BoundarizeOperator(self.boundaryconditions_tag['stressfree'])(q) q_bc_stressfree_null = BoundarizeOperator(self.boundaryconditions_tag['stressfree'])(0) q_bc_fixed = BoundarizeOperator(self.boundaryconditions_tag['fixed'])(q) q_bc_fixed_null = BoundarizeOperator(self.boundaryconditions_tag['fixed'])(0) all_tags_and_bcs = [ (self.boundaryconditions_tag['stressfree'], q_bc_stressfree, q_bc_stressfree_null), (self.boundaryconditions_tag['fixed'], q_bc_fixed, q_bc_fixed_null) ] bdry_tag_state_flux = [(tag, bc, self.bdry_flux(bc, bc_null, tag)) for tag, bc, bc_null in all_tags_and_bcs] # Entire operator nabla = make_nabla(dim) res_q = (numpy.dot(nabla, fluxes) + InverseMassOperator() * (self.flux_num(q, fluxes, bdry_tag_state_flux))) res_q = self.add_sources(res_q) F2 = self.F2(w) P = self.P(q) v = self.v(q) if dim == 1: F = [P[0],v[0]] elif dim == 2: F = [P[0],P[2],v[0],v[1], P[2],P[1],v[1],v[0]] elif dim == 3: F = [P[0],P[5],P[4],v[0],v[2],v[1], P[5],P[1],P[3],v[1],v[2],v[0], P[4],P[3],P[2],v[2],v[1],v[0]] else: raise ValueError("Invalid dimension") res_f2 = numpy.zeros(self.len_f2, dtype=object) for i in range(dim): for j in range(dim*2): res_f2[i*dim*2+j] = (-1)*F2[i*dim*2+j]*alpha[i]-sigma[i]/kappa[i]*(F2[i*dim*2+j]+F[i*dim*2+j]) return join_fields(res_q, res_f2)
def sym_operator(self): d = self.ambient_dim w = sym.make_sym_array("w", d + 1) u = w[0] v = w[1:] # boundary conditions ------------------------------------------------- # dirichlet BCs ------------------------------------------------------- dir_u = sym.cse(sym.interp("vol", self.dirichlet_tag)(u)) dir_v = sym.cse(sym.interp("vol", 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 = sym.Field("dir_bc_u") dir_bc = join_fields(2 * dir_g - dir_u, dir_v) else: dir_bc = join_fields(-dir_u, dir_v) dir_bc = sym.cse(dir_bc, "dir_bc") # neumann BCs --------------------------------------------------------- neu_u = sym.cse(sym.interp("vol", self.neumann_tag)(u)) neu_v = sym.cse(sym.interp("vol", self.neumann_tag)(v)) neu_bc = sym.cse(join_fields(neu_u, -neu_v), "neu_bc") # radiation BCs ------------------------------------------------------- rad_normal = sym.normal(self.radiation_tag, d) rad_u = sym.cse(sym.interp("vol", self.radiation_tag)(u)) rad_v = sym.cse(sym.interp("vol", self.radiation_tag)(v)) rad_bc = sym.cse( join_fields( 0.5 * (rad_u - self.sign * np.dot(rad_normal, rad_v)), 0.5 * rad_normal * (np.dot(rad_normal, rad_v) - self.sign * rad_u)), "rad_bc") # entire operator ----------------------------------------------------- def flux(pair): return sym.interp(pair.dd, "all_faces")(self.flux(pair)) result = sym.InverseMassOperator()( join_fields(-self.c * np.dot(sym.stiffness_t(self.ambient_dim), v), -self.c * (sym.stiffness_t(self.ambient_dim) * u)) - sym.FaceMassOperator() (flux(sym.int_tpair(w)) + flux(sym.bv_tpair(self.dirichlet_tag, w, dir_bc)) + flux(sym.bv_tpair(self.neumann_tag, w, neu_bc)) + flux(sym.bv_tpair(self.radiation_tag, w, rad_bc)))) result[0] += self.source_f return result
def flux(self, w): """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. """ normal = sym.normal(w.dd, self.dimensions) if self.fixed_material: e, h = self.split_eh(w) epsilon = self.epsilon mu = self.mu Z_int = (mu/epsilon)**0.5 # noqa: N806 Y_int = 1/Z_int # noqa: N806 Z_ext = (mu/epsilon)**0.5 # noqa: N806 Y_ext = 1/Z_ext # noqa: N806 if self.flux_type == "lf": # if self.fixed_material: # max_c = (self.epsilon*self.mu)**(-0.5) 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*e.int - epsilon*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*h.int - mu*h.ext) )) elif isinstance(self.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) - self.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) + self.flux_type*self.space_cross_h(normal, h.int-h.ext)) ), ) else: raise ValueError("maxwell: invalid flux_type (%s)" % self.flux_type)
def __call__(self, x, y, three_mult=None): """Compute the subsetted cross product on the indexables *x* and *y*. :param three_mult: a function of three arguments *sign, xj, yk* used in place of the product *sign*xj*yk*. Defaults to just this product if not given. """ from pytools.obj_array import join_fields if three_mult is None: return join_fields(*[f(x, y) for f in self.functions]) else: return join_fields( *[sum(three_mult(lc, x[j], y[k]) for lc, j, k in lcjk) for lcjk in self.component_lcjk])
def __call__(self, x, y, three_mult=None): """Compute the subsetted cross product on the indexables *x* and *y*. :param three_mult: a function of three arguments *sign, xj, yk* used in place of the product *sign*xj*yk*. Defaults to just this product if not given. """ from pytools.obj_array import join_fields if three_mult is None: return join_fields(*[f(x, y) for f in self.functions]) else: return join_fields(*[ sum(three_mult(lc, x[j], y[k]) for lc, j, k in lcjk) for lcjk in self.component_lcjk ])
def flux(self, w): u = w[0] v = w[1:] normal = sym.normal(w.dd, self.ambient_dim) flux_weak = join_fields(np.dot(v.avg, normal), u.avg * normal) if self.flux_type == "central": return -self.c * flux_weak elif self.flux_type == "upwind": return -self.c * (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)
def noslip_state(self, state): from hedge.optemplate import make_normal state0 = join_fields(make_sym_vector("bc_q_noslip", 2), [0] * self.dimensions) normal = make_normal(self.noslip_tag, self.dimensions) bc = self.make_bc_info("bc_q_noslip", self.noslip_tag, state, state0) return self.inflow_state_inner(normal, bc, "noslip")
def absorbing_bc(self, w): """Construct part of the flux operator template for 1st order absorbing boundary conditions. """ absorb_normal = sym.normal(self.absorb_tag, self.dimensions) e, h = self.split_eh(w) if self.fixed_material: epsilon = self.epsilon mu = self.mu absorb_Z = (mu/epsilon)**0.5 # noqa: N806 absorb_Y = 1/absorb_Z # noqa: N806 absorb_e = sym.cse(sym.interp("vol", self.absorb_tag)(e)) absorb_h = sym.cse(sym.interp("vol", 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 get_spherical_coord(x_vec): """ :param x_vec: is an array whose leading dimension iterates over the X, Y, Z axes, and whose further dimensions may iterate over a number of points. :returns: object array of [r, phi, theta]. phi is the angle in (x,y) in :math:`(-\\pi,\\pi)`. """ if len(x_vec) != 3: raise ValueError("only 3-d arrays are supported") x = x_vec[0] y = x_vec[1] z = x_vec[2] r = numpy.sqrt(x**2 + y**2 + z**2) from warnings import warn if (numpy.any(r < numpy.power(10.0, -10.0))): warn('spherical coordinate transformation ill-defined at r=0') phi = numpy.arctan2(y, x) theta = numpy.arccos(z / r) from pytools.obj_array import join_fields return join_fields(r, phi, theta)
def integral_equation(self, r_tilde, q_tilde, hvf_coefficients): fix = 0 if self.invertible: s_ones = cse(S(0, Ones()), "s_ones") def inv_rank_one_coeff(u): return cse(Mean(cse(S(0, cse(S(0, u)))))) r_coeff = inv_rank_one_coeff(r_tilde) q_coeff = inv_rank_one_coeff(q_tilde) from pytools.obj_array import join_fields factors = self.cluster_points() fix = join_fields( factors[0] * s_ones * (r_coeff), factors[1] * Ones() * (q_coeff), ) return DebyeOperatorBase.integral_equation(self, r_tilde, q_tilde, hvf_coefficients, fix=fix)
def get_spherical_coord(x_vec): """ :param x_vec: is an array whose leading dimension iterates over the X, Y, Z axes, and whose further dimensions may iterate over a number of points. :returns: object array of [r, phi, theta]. phi is the angle in (x,y) in :math:`(-\\pi,\\pi)`. """ if len(x_vec) != 3: raise ValueError("only 3-d arrays are supported") x = x_vec[0] y = x_vec[1] z = x_vec[2] r = numpy.sqrt(x**2+y**2+z**2) from warnings import warn if(numpy.any(r<numpy.power(10.0,-10.0))): warn('spherical coordinate transformation ill-defined at r=0') phi = numpy.arctan2(y,x) theta = numpy.arccos(z/r) from pytools.obj_array import join_fields return join_fields(r,phi,theta)
def set_up_stepper(self, discr, field_var_name, sym_rhs, num_fields, function_registry=base_function_registry, exec_mapper_factory=ExecutionMapper): dt_method = LSRK4MethodBuilder(component_id=field_var_name) dt_code = dt_method.generate() self.field_var_name = field_var_name self.state_name = f"input_{field_var_name}" # Transcribe the phase. output_vars, results, yielded_states = transcribe_phase( dt_code, field_var_name, num_fields, "primary", sym_rhs) # Build the bound operator for the time integrator. output_t = results[0] output_dt = results[1] output_states = results[2] output_residuals = results[3] assert len(output_states) == num_fields assert len(output_states) == len(output_residuals) from pytools.obj_array import join_fields flattened_results = join_fields(output_t, output_dt, *output_states) self.bound_op = bind(discr, flattened_results, function_registry=function_registry, exec_mapper_factory=exec_mapper_factory)
def pmc_bc(self, w): "Construct part of the flux operator template for PMC boundary conditions" e, h = self.split_eh(w) pmc_e = sym.cse(sym.interp("vol", self.pmc_tag)(e)) pmc_h = sym.cse(sym.interp("vol", self.pmc_tag)(h)) return join_fields(pmc_e, -pmc_h)
def map_numpy_array(self, expr): if len(expr.shape) != 1: raise ValueError("only 1D numpy arrays are supported") from pytools.obj_array import join_fields return join_fields(*[ self.rec(expr[i]) for i in range(len(expr))])
def wave_operator(discr, c, w): u = w[0] v = w[1:] dir_u = discr.interp("vol", BTAG_ALL, u) dir_v = discr.interp("vol", BTAG_ALL, v) dir_bval = join_fields(dir_u, dir_v) dir_bc = join_fields(-dir_u, dir_v) return ( -join_fields(-c * discr.div(v), -c * discr.grad(u)) + # noqa: W504 discr.inverse_mass( discr.face_mass( wave_flux(discr, c=c, w_tpair=interior_trace_pair(discr, w)) + wave_flux( discr, c=c, w_tpair=TracePair(BTAG_ALL, dir_bval, dir_bc))) ))
def noslip_state(self, state): from hedge.optemplate import make_normal state0 = join_fields( make_sym_vector("bc_q_noslip", 2), [0]*self.dimensions) normal = make_normal(self.noslip_tag, self.dimensions) bc = self.make_bc_info("bc_q_noslip", self.noslip_tag, state, state0) return self.inflow_state_inner(normal, bc, "noslip")
def main(): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) target_order = 10 from functools import partial nelements = 30 qbx_order = 4 from sumpy.kernel import LaplaceKernel from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, make_curve_mesh) mesh = make_curve_mesh(partial(ellipse, 1), np.linspace(0, 1, nelements+1), target_order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory density_discr = Discretization(cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource(density_discr, 4*target_order, qbx_order, fmm_order=False) from pytools.obj_array import join_fields sig_sym = sym.var("sig") knl = LaplaceKernel(2) op = join_fields( sym.tangential_derivative(mesh.ambient_dim, sym.D(knl, sig_sym, qbx_forced_limit=+1)).as_scalar(), sym.tangential_derivative(mesh.ambient_dim, sym.D(knl, sig_sym, qbx_forced_limit=-1)).as_scalar(), ) nodes = density_discr.nodes().with_queue(queue) angle = cl.clmath.atan2(nodes[1], nodes[0]) n = 10 sig = cl.clmath.sin(n*angle) dt_sig = n*cl.clmath.cos(n*angle) res = bind(qbx, op)(queue, sig=sig) extval = res[0].get() intval = res[1].get() pv = 0.5*(extval + intval) dt_sig_h = dt_sig.get() import matplotlib.pyplot as pt pt.plot(extval, label="+num") pt.plot(pv + dt_sig_h*0.5, label="+ex") pt.legend(loc="best") pt.show()
def test_stepper_mem_ops(ctx_factory, use_fusion): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) dims = 2 sym_operator, discr = get_strong_wave_op_with_discr_direct(cl_ctx, dims=dims, order=3) t_start = 0 dt = 0.04 t_end = 0.2 from pytools.obj_array import join_fields ic = join_fields(discr.zeros(queue), [discr.zeros(queue) for i in range(discr.dim)]) if not use_fusion: bound_op = bind(discr, sym_operator, exec_mapper_factory=ExecutionMapperWithMemOpCounting) stepper = RK4TimeStepper( queue, discr, "w", bound_op, 1 + discr.dim, get_strong_wave_component, exec_mapper_factory=ExecutionMapperWithMemOpCounting) else: stepper = FusedRK4TimeStepper( queue, discr, "w", sym_operator, 1 + discr.dim, get_strong_wave_component, exec_mapper_factory=ExecutionMapperWithMemOpCounting) step = 0 nsteps = int(np.ceil((t_end + 1e-9) / dt)) for (_, _, profile_data) in stepper.run(ic, t_start, dt, t_end, return_profile_data=True): step += 1 logger.info("step %d/%d", step, nsteps) logger.info("using fusion? %s", use_fusion) logger.info("bytes read: %d", profile_data["bytes_read"]) logger.info("bytes written: %d", profile_data["bytes_written"]) logger.info("bytes total: %d", profile_data["bytes_read"] + profile_data["bytes_written"])
def _bound_op(self, queue, t, *args, profile_data=None): from pytools.obj_array import join_fields context = {"t": t, self.field_var_name: join_fields(*args)} result = self.grudge_bound_op(queue, profile_data=profile_data, **context) if profile_data is not None: result = result[0] return result
def conservative_to_primitive(self, q, use_cses=True): if use_cses: from hedge.optemplate.primitives import make_common_subexpression as cse else: def cse(x, name): return x return join_fields(self.rho(q), self.p(q), self.u(q))
def conservative_to_primitive(self, q, use_cses=True): if use_cses: from hedge.optemplate.primitives import make_common_subexpression as cse else: def cse(x, name): return x return join_fields( self.rho(q), self.p(q), self.u(q))
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))
def flux(self, w, k): F2 = self.F2(w) #get F'' from state vector w q = self.q(w) P = self.P(q) v = self.v(q) v_null = Field('state_null') dim = self.dimensions # One entry for each flux direction if dim == 1: raise NotImplementedError #return [cse(join_fields( # v_null, # (P[0]+F2[0])/k[0], # flux rho_v # (v[0]+F2[1])/k[0] # flux F # ), "x_flux")] elif dim == 2: return [cse(join_fields( v_null, (P[0]+F2[0])/k[0],(P[3]+F2[1])/k[0], # flux rho_v (v[0]+F2[2])/k[0],v_null,v_null,(v[1]+F2[3])/k[0] # flux F ), "x_flux"), cse(join_fields( v_null, (P[2]+F2[4])/k[1],(P[1]+F2[5])/k[1], # flux rho_v v_null,(v[1]+F2[6])/k[1],(v[0]+F2[7])/k[1],v_null # flux F ), "y_flux")] elif dim == 3: raise NotImplementedError #return [cse(join_fields( # v_null, (P[0]+F2[0])/k[0],(P[8]+F2[1])/k[0],(P[7]+F2[2])/k[0], # flux rho_v # (v[0]+F2[3])/k[0],v_null,v_null,v_null,v_null,v_null,v_null,(v[2]+F2[4])/k[0],(v[1]+F2[5])/k[0] # flux F # ), "x_flux"), # cse(join_fields( # v_null, (P[5]+F2[6])/k[1],(P[1]+F2[7])/k[1],(P[6]+F2[8])/k[1], # flux rho_v # v_null,(v[1]+F2[9])/k[1],v_null,v_null,v_null,(v[0]+F2[10])/k[1],(v[2]+F2[11])/k[1],v_null,v_null # flux F # ), "y_flux"), # cse(join_fields( # v_null, (P[4]+F2[12])/k[2],(P[3]+F2[13])/k[2],(P[2]+F2[14])/k[2], # flux rho_v # v_null,v_null,(v[2]+F2[15])/k[2],(v[1]+F2[16])/k[2],(v[0]+F2[17])/k[2],v_null,v_null,v_null,v_null # flux F # ), "z_flux")] else: raise ValueError("Invalid dimension")
def flux(self, w): c = w[0] u = w[1] v = w[2:] normal = sym.normal(w.dd, self.ambient_dim) if self.flux_type == "central": return -0.5 * join_fields( np.dot(v.int * c.int + v.ext * c.ext, normal), (u.int * c.int + u.ext * c.ext) * normal) elif self.flux_type == "upwind": return -0.5 * join_fields( np.dot(normal, c.ext * v.ext + c.int * v.int) + c.ext * u.ext - c.int * u.int, normal * (np.dot(normal, c.ext * v.ext - c.int * v.int) + c.ext * u.ext + c.int * u.int)) else: raise ValueError("invalid flux type '%s'" % self.flux_type)
def __call__(self, fields): from hedge.tools import join_fields #get conserved fields rho=self.op.rho(fields) e=self.op.e(fields) rho_velocity=self.op.rho_u(fields) #get primitive fields #to do #reset field values to cell average rhoLim=self.get_average(rho) eLim=self.get_average(e) temp=join_fields([self.get_average(rho_vel) for rho_vel in rho_velocity]) #should do for primitive fields too return join_fields(rhoLim, eLim, temp)
def __call__(self, fields): from grudge.tools import join_fields #get conserved fields rho=self.op.rho(fields) e=self.op.e(fields) rho_velocity=self.op.rho_u(fields) #get primitive fields #to do #reset field values to cell average rhoLim=self.get_average(rho) eLim=self.get_average(e) temp=join_fields([self.get_average(rho_vel) for rho_vel in rho_velocity]) #should do for primitive fields too return join_fields(rhoLim, eLim, temp)
def make_field_vector(name, components): """Return an object array of *components* subscripted :class:`Variable` instances. :param components: The number of components in the vector. """ if isinstance(components, int): components = list(range(components)) from pytools.obj_array import join_fields vfld = Field(name) return join_fields(*[vfld.index(i) for i in components])
def flux(self, w): u = w[0] v = w[1:] normal = sym.normal(w.dd, self.ambient_dim) 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/grudge-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 make_sym_vector(name, components): """Return an object array of *components* subscripted :class:`Field` instances. :param components: The number of components in the vector. """ if isinstance(components, int): components = range(components) from pytools.obj_array import join_fields vfld = Variable(name) return join_fields(*[vfld[i] for i in components])
def wall_state(self, state): from grudge.symbolic import RestrictToBoundary bc = RestrictToBoundary(self.wall_tag)(state) wall_rho = self.rho(bc) wall_e = self.e(bc) # <3 eve wall_rho_u = self.rho_u(bc) from grudge.symbolic import make_normal normal = make_normal(self.wall_tag, self.dimensions) return join_fields( wall_rho, wall_e, wall_rho_u - 2 * numpy.dot(wall_rho_u, normal) * normal)
def bump(discr, queue, t=0): source_center = np.array([0.0, 0.05]) source_width = 0.05 source_omega = 3 nodes = discr.volume_discr.nodes().with_queue(queue) center_dist = join_fields([ nodes[0] - source_center[0], nodes[1] - source_center[1], ]) return (np.cos(source_omega * t) * clmath.exp(-np.dot(center_dist, center_dist) / source_width**2))
def wall_state(self, state): from hedge.optemplate import BoundarizeOperator bc = BoundarizeOperator(self.wall_tag)(state) wall_rho = self.rho(bc) wall_e = self.e(bc) # <3 eve wall_rho_u = self.rho_u(bc) from hedge.optemplate import make_normal normal = make_normal(self.wall_tag, self.dimensions) return join_fields( wall_rho, wall_e, wall_rho_u - 2 * numpy.dot(wall_rho_u, normal) * normal)
def inflow_state_inner(self, normal, bc, name): # see grudge/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 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 wall_state(self, state): from hedge.optemplate import BoundarizeOperator bc = BoundarizeOperator(self.wall_tag)(state) wall_rho = self.rho(bc) wall_e = self.e(bc) # <3 eve wall_rho_u = self.rho_u(bc) from hedge.optemplate import make_normal normal = make_normal(self.wall_tag, self.dimensions) return join_fields( wall_rho, wall_e, wall_rho_u - 2*numpy.dot(wall_rho_u, normal) * normal)
def primitive_to_conservative(self, prims, use_cses=True): if not use_cses: from hedge.optemplate.primitives 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 __init__(self, queue, discr, field_var_name, grudge_bound_op, num_fields, component_getter, exec_mapper_factory=ExecutionMapper): """Arguments: field_var_name: The name of the simulation variable grudge_bound_op: The BoundExpression for the right-hand side num_fields: The number of components in the simulation variable component_getter: A function, which, given an object array representing the simulation variable, splits the array into its components """ super().__init__(queue, component_getter) # Construct sym_rhs to have the effect of replacing the RHS calls in the # dagrt code with calls of the grudge operator. from grudge.symbolic.primitives import FunctionSymbol, Variable call = sym.cse( FunctionSymbol("grudge_op")( *((Variable("t", dd=sym.DD_SCALAR), ) + tuple( Variable(field_var_name, dd=sym.DD_VOLUME)[i] for i in range(num_fields))))) from pytools.obj_array import join_fields sym_rhs = join_fields(*(call[i] for i in range(num_fields))) self.queue = queue self.grudge_bound_op = grudge_bound_op from grudge.function_registry import register_external_function freg = register_external_function(base_function_registry, "grudge_op", implementation=self._bound_op, dd=sym.DD_VOLUME) self.set_up_stepper(discr, field_var_name, sym_rhs, num_fields, freg, exec_mapper_factory) self.component_getter = component_getter
def primitive_to_conservative(self, prims, use_cses=True): if not use_cses: from hedge.optemplate.primitives 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 wave_flux(discr, c, w_tpair): u = w_tpair[0] v = w_tpair[1:] normal = with_queue(u.int.queue, discr.normal(w_tpair.where)) flux_weak = join_fields(np.dot(v.avg, normal), normal[0] * u.avg, normal[1] * u.avg) # upwind v_jump = np.dot(normal, v.int - v.ext) flux_weak -= join_fields( 0.5 * (u.int - u.ext), 0.5 * normal[0] * v_jump, 0.5 * normal[1] * v_jump, ) flux_strong = join_fields( np.dot(v.int, normal), u.int * normal[0], u.int * normal[1], ) - flux_weak return discr.interp(w_tpair.where, "all_faces", c * flux_strong)
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 volume_field_base(self, r, q, j): k = self.k grad_phi = grad_S(k, r, 3) grad_psi = grad_S(k, q, 3) m = cse(self.m(j), "m") A = cse(S(k, j), "A") Q = cse(S(k, m), "Q") E = 1j * k * A - grad_phi - curl_S_volume(k, m) H = curl_S_volume(k, j) + 1j * k * Q - grad_psi from pytools.obj_array import join_fields return join_fields(E, H)
def volume_field_base(self, r, q, j): k = self.k grad_phi = grad_S(k, r, 3) grad_psi = grad_S(k, q, 3) m = cse(self.m(j), "m") A = cse(S(k, j), "A") Q = cse(S(k, m), "Q") E = 1j*k*A - grad_phi - curl_S_volume(k, m) H = curl_S_volume(k, j) + 1j*k*Q - grad_psi from pytools.obj_array import join_fields return join_fields(E, H)
def make_sym_vector(name, components, var_factory=None): """Return an object array of *components* subscripted :class:`Variable` (or subclass) instances. :arg components: The number of components in the vector. :arg var_factory: The :class:`Variable` subclass to use for instantiating the scalar variables. """ if var_factory is None: var_factory = Variable if isinstance(components, int): components = list(range(components)) from pytools.obj_array import join_fields vfld = var_factory(name) return join_fields(*[vfld.index(i) for i in components])
def integral_equation(self, *args, **kwargs): nxnxE, ndotH = self.boundary_field(*args) nxnxE = cse(nxnxE, "nxnxE") fix = kwargs.pop("fix", 0) if kwargs: raise TypeError("invalid keyword argument(s)") from pytools.obj_array import make_obj_array eh_op = make_obj_array([ 2*_debye_S0_surf_div(nxnxE), -ndotH, ]) + fix k = self.k j = cse(self.j(*args), "j") m = cse(self.m(j), "m") A = cse(S(k, j), "A") E_minus_grad_phi = 1j*k*A - curl_S_volume(k, m) from hellskitchen.fmm import DifferenceKernel from pytools.obj_array import join_fields return join_fields( eh_op, # FIXME: These are inefficient. They compute a full volume field, # but only actually use the line part of it. [ # Grad phi integrated on a loop does not contribute. LineIntegral(E_minus_grad_phi, a_cycle_name) for a_cycle_name in self.a_cycle_names ], [ LineIntegral( # (E(k) - E(0))/(1jk) # Grad phi integrated on a loop does not contribute. (1j*k*A - curl_S_volume(DifferenceKernel(k), m)) /(1j*k), b_cycle_name) for b_cycle_name in self.b_cycle_names ] )
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 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 bdry_flux(self, q_bdry, q_null, tag): if tag == self.boundaryconditions_tag['stressfree']: signP = -1 signv = 1 elif tag == self.boundaryconditions_tag['fixed']: signP = 1 signv = -1 else: raise ValueError("Invalid boundary conditions") dim = self.dimensions P = self.P(q_bdry) v = self.v(q_bdry) v_null = q_null # One entry for each flux direction if dim == 1: return [cse(join_fields( v_null, signP*P[0], # flux rho_v signv*v[0] # flux F ), "x_bflux")] elif dim == 2: return [cse(join_fields( v_null, signP*P[0],signP*P[2], # flux rho_v signv*v[0],v_null,v_null,signv*v[1] # flux F ), "x_bflux"), cse(join_fields( v_null, signP*P[2],signP*P[1], # flux rho_v v_null,signv*v[1],signv*v[0],v_null # flux F ), "y_bflux")] elif dim == 3: return [cse(join_fields( v_null, signP*P[0],signP*P[5],signP*P[4], # flux rho_v signv*v[0],v_null,v_null,v_null,v_null,v_null,signv*v[2],v_null,signv*v[1] # flux F ), "x_bflux"), cse(join_fields( v_null, signP*P[5],signP*P[1],signP*P[3], # flux rho_v v_null,signv*v[1],v_null,v_null,signv*v[2],v_null,v_null,signv*v[0],v_null # flux F ), "y_bflux"), cse(join_fields( v_null, signP*P[4],signP*P[3],signP*P[2], # flux rho_v v_null,v_null,signv*v[2],signv*v[1],v_null,signv*v[0],v_null,v_null,v_null # flux F ), "z_bflux")] else: raise ValueError("Invalid dimension")
def make_second_order_part(self): state = self.state() faceq_state = self.faceq_state() volq_state = self.volq_state() volq_tau_mat = self.tau(to_vol_quad, state) faceq_tau_mat = self.tau(to_int_face_quad, state) return join_fields( 0, self.div( numpy.sum(volq_tau_mat*self.cse_u(volq_state), axis=1) + self.heat_conduction_grad(to_vol_quad) , numpy.sum(faceq_tau_mat*self.cse_u(faceq_state), axis=1) + self.heat_conduction_grad(to_int_face_quad) , ), [ self.div(volq_tau_mat[i], faceq_tau_mat[i]) for i in range(self.dimensions)] )
def integral_equation(self, r_tilde, q_tilde, hvf_coefficients): fix = 0 if self.invertible: s_ones = cse(S(0, Ones()), "s_ones") def inv_rank_one_coeff(u): return cse(Mean(cse(S(0, cse(S(0, u)))))) r_coeff = inv_rank_one_coeff(r_tilde) q_coeff = inv_rank_one_coeff(q_tilde) from pytools.obj_array import join_fields factors = self.cluster_points() fix = join_fields( factors[0]*s_ones*(r_coeff), factors[1]*Ones()*(q_coeff), ) return DebyeOperatorBase.integral_equation( self, r_tilde, q_tilde, hvf_coefficients, fix=fix)
def flux(self, q): P = self.P(q) v = self.v(q) v_null = Field('state_null') dim = self.dimensions # One entry for each flux direction if dim == 1: return [cse(join_fields( v_null, P[0], # flux rho_v v[0] # flux F ), "x_flux")] elif dim == 2: return [cse(join_fields( v_null, P[0],P[2],# flux rho_v v[0],v_null,v[1] # flux F ), "x_flux"), cse(join_fields( v_null, P[2],P[1],# flux rho_v v_null,v[1],v[0] # flux F ), "y_flux")] elif dim == 3: return [cse(join_fields( v_null, P[0],P[5],P[4], # flux rho_v v[0],v_null,v_null,v_null,v[2],v[1] # flux F ), "x_flux"), cse(join_fields( v_null, P[5],P[1],P[3], # flux rho_v v_null,v[1],v_null,v[2],v_null,v[0] # flux F ), "y_flux"), cse(join_fields( v_null, P[4],P[3],P[2], # flux rho_v v_null,v_null,v[2],v[1],v[0],v_null # flux F ), "z_flux")] else: raise ValueError("Invalid dimension")
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_sym_vector("source_vect", len(self.state())), # extra field for speed 0) return result