def wave_operator(discr, c, w): u = w[0] v = w[1:] dir_u = discr.project("vol", BTAG_ALL, u) dir_v = discr.project("vol", BTAG_ALL, v) dir_bval = flat_obj_array(dir_u, dir_v) dir_bc = flat_obj_array(-dir_u, dir_v) dd_quad = DOFDesc("vol", "vel_prod") c_quad = discr.project("vol", dd_quad, c) w_quad = discr.project("vol", dd_quad, w) u_quad = w_quad[0] v_quad = w_quad[1:] dd_allfaces_quad = DOFDesc("all_faces", "vel_prod") # FIXME Fix sign issue return (discr.inverse_mass( flat_obj_array(discr.weak_div(dd_quad, scalar(c_quad) * v_quad), discr.weak_grad(dd_quad, c_quad * u_quad)) - # noqa: W504 discr.face_mass( dd_allfaces_quad, 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 wave_flux(discr, c, w_tpair): dd = w_tpair.dd dd_quad = dd.with_qtag("vel_prod") u = w_tpair[0] v = w_tpair[1:] normal = thaw(u.int.array_context, discr.normal(dd)) flux_weak = flat_obj_array( np.dot(v.avg, normal), normal * scalar(u.avg), ) # upwind v_jump = np.dot(normal, v.int - v.ext) flux_weak -= flat_obj_array( 0.5 * (u.int - u.ext), 0.5 * normal * scalar(v_jump), ) # FIMXE this flux is only correct for continuous c dd_allfaces_quad = dd_quad.with_dtag("all_faces") c_quad = discr.project("vol", dd_quad, c) flux_quad = discr.project(dd, dd_quad, flux_weak) return discr.project(dd_quad, dd_allfaces_quad, scalar(c_quad) * flux_quad)
def test_wave_stability(actx_factory, problem, timestep_scale, order, visualize=False): """Checks stability of the wave operator for a given problem setup. Adjust *timestep_scale* to get timestep close to stability limit. """ actx = actx_factory() p = problem sym_u, sym_v, sym_f, sym_rhs = sym_wave(p.dim, p.sym_phi) mesh = p.mesh_factory(8) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) def sym_eval(expr, t): return sym.EvaluationMapper({"c": p.c, "x": nodes, "t": t})(expr) def get_rhs(t, w): result = wave_operator(discr, c=p.c, w=w) result[0] += sym_eval(sym_f, t) return result t = 0. u = sym_eval(sym_u, t) v = sym_eval(sym_v, t) fields = flat_obj_array(u, v) from mirgecom.integrators import rk4_step dt = timestep_scale / order**2 for istep in range(10): fields = rk4_step(fields, t, dt, get_rhs) t += dt expected_u = sym_eval(sym_u, 10 * dt) expected_v = sym_eval(sym_v, 10 * dt) expected_fields = flat_obj_array(expected_u, expected_v) if visualize: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, discr.order) vis.write_vtk_file("wave_stability.vtu", [ ("u", fields[0]), ("v", fields[1:]), ("u_expected", expected_fields[0]), ("v_expected", expected_fields[1:]), ]) err = discr.norm(fields - expected_fields, np.inf) max_err = discr.norm(expected_fields, np.inf) assert err < max_err
def wave_flux(dcoll, c, w_tpair): dd = w_tpair.dd dd_quad = dd.with_discr_tag(DISCR_TAG_QUAD) u = w_tpair[0] v = w_tpair[1:] normal = thaw(u.int.array_context, op.normal(dcoll, dd)) flux_weak = flat_obj_array( np.dot(v.avg, normal), normal * u.avg, ) # upwind flux_weak += flat_obj_array( 0.5 * (u.ext - u.int), 0.5 * normal * np.dot(normal, v.ext - v.int), ) # FIXME this flux is only correct for continuous c dd_allfaces_quad = dd_quad.with_dtag("all_faces") c_quad = op.project(dcoll, "vol", dd_quad, c) flux_quad = op.project(dcoll, dd, dd_quad, flux_weak) return op.project(dcoll, dd_quad, dd_allfaces_quad, c_quad * flux_quad)
def wave_operator(discr, c, w): u = w[0] v = w[1:] dir_u = discr.project("vol", BTAG_ALL, u) dir_v = discr.project("vol", BTAG_ALL, v) dir_bval = flat_obj_array(dir_u, dir_v) dir_bc = flat_obj_array(-dir_u, dir_v) return ( discr.inverse_mass( flat_obj_array( -c*discr.weak_div(v), -c*discr.weak_grad(u) ) + # noqa: W504 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, interior=dir_bval, exterior=dir_bc)) + sum( wave_flux(discr, c=c, w_tpair=tpair) for tpair in cross_rank_trace_pairs(discr, w)) ) ) )
def wave_operator(dcoll, c, w): u = w[0] v = w[1:] dir_u = op.project(dcoll, "vol", BTAG_ALL, u) dir_v = op.project(dcoll, "vol", BTAG_ALL, v) dir_bval = flat_obj_array(dir_u, dir_v) dir_bc = flat_obj_array(-dir_u, dir_v) dd_quad = DOFDesc("vol", DISCR_TAG_QUAD) c_quad = op.project(dcoll, "vol", dd_quad, c) w_quad = op.project(dcoll, "vol", dd_quad, w) u_quad = w_quad[0] v_quad = w_quad[1:] dd_allfaces_quad = DOFDesc("all_faces", DISCR_TAG_QUAD) return (op.inverse_mass( dcoll, flat_obj_array(-op.weak_local_div(dcoll, dd_quad, c_quad * v_quad), -op.weak_local_grad(dcoll, dd_quad, c_quad * u_quad)) + # noqa: W504 op.face_mass( dcoll, dd_allfaces_quad, wave_flux(dcoll, c=c, w_tpair=op.interior_trace_pair(dcoll, w)) + wave_flux(dcoll, c=c, w_tpair=TracePair( BTAG_ALL, interior=dir_bval, exterior=dir_bc)))))
def flux(self, wtpair): """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. """ actx = get_container_context_recursively(wtpair) normal = thaw(self.dcoll.normal(wtpair.dd), actx) if self.fixed_material: e, h = self.split_eh(wtpair) 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 flat_obj_array( # flux e, 1 / 2 * ( -self.space_cross_h(normal, h.ext - h.int) # 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.ext - e.int) # 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 flat_obj_array( # flux e, (-1 / (Z_int + Z_ext) * self.space_cross_h( normal, Z_ext * (h.ext - h.int) - self.flux_type * self.space_cross_e(normal, e.ext - e.int)) ), # flux h (1 / (Y_int + Y_ext) * self.space_cross_e( normal, Y_ext * (e.ext - e.int) + self.flux_type * self.space_cross_h(normal, h.ext - h.int)) ), ) else: raise ValueError("maxwell: invalid flux_type (%s)" % self.flux_type)
def get_rectangular_cavity_mode(E_0, mode_indices): # noqa: N803 """A rectangular TM cavity mode for a rectangle / cube with one corner at the origin and the other at (1,1[,1]).""" dims = len(mode_indices) if dims != 2 and dims != 3: raise ValueError("Improper mode_indices dimensions") import numpy factors = [n * numpy.pi for n in mode_indices] kx, ky = factors[0:2] if dims == 3: kz = factors[2] omega = numpy.sqrt(sum(f**2 for f in factors)) nodes = sym.nodes(dims) x = nodes[0] y = nodes[1] if dims == 3: z = nodes[2] sx = sym.sin(kx * x) cx = sym.cos(kx * x) sy = sym.sin(ky * y) cy = sym.cos(ky * y) if dims == 3: sz = sym.sin(kz * z) cz = sym.cos(kz * z) if dims == 2: tfac = sym.ScalarVariable("t") * omega result = flat_obj_array( 0, 0, sym.sin(kx * x) * sym.sin(ky * y) * sym.cos(tfac), # ez -ky * sym.sin(kx * x) * sym.cos(ky * y) * sym.sin(tfac) / omega, # hx kx * sym.cos(kx * x) * sym.sin(ky * y) * sym.sin(tfac) / omega, # hy 0, ) else: tdep = sym.exp(-1j * omega * sym.ScalarVariable("t")) gamma_squared = ky**2 + kx**2 result = flat_obj_array( -kx * kz * E_0 * cx * sy * sz * tdep / gamma_squared, # ex -ky * kz * E_0 * sx * cy * sz * tdep / gamma_squared, # ey E_0 * sx * sy * cz * tdep, # ez -1j * omega * ky * E_0 * sx * cy * cz * tdep / gamma_squared, # hx 1j * omega * kx * E_0 * cx * sy * cz * tdep / gamma_squared, 0, ) return result
def get_rectangular_cavity_mode(actx, nodes, t, E_0, mode_indices): # noqa: N803 """A rectangular TM cavity mode for a rectangle / cube with one corner at the origin and the other at (1,1[,1]).""" dims = len(mode_indices) if dims != 2 and dims != 3: raise ValueError("Improper mode_indices dimensions") factors = [n * np.pi for n in mode_indices] kx, ky = factors[0:2] if dims == 3: kz = factors[2] omega = np.sqrt(sum(f**2 for f in factors)) x = nodes[0] y = nodes[1] if dims == 3: z = nodes[2] zeros = 0 * x sx = actx.np.sin(kx * x) cx = actx.np.cos(kx * x) sy = actx.np.sin(ky * y) cy = actx.np.cos(ky * y) if dims == 3: sz = actx.np.sin(kz * z) cz = actx.np.cos(kz * z) if dims == 2: tfac = t * omega result = flat_obj_array( zeros, zeros, actx.np.sin(kx * x) * actx.np.sin(ky * y) * np.cos(tfac), # ez (-ky * actx.np.sin(kx * x) * actx.np.cos(ky * y) * np.sin(tfac) / omega), # hx (kx * actx.np.cos(kx * x) * actx.np.sin(ky * y) * np.sin(tfac) / omega), # hy zeros, ) else: tdep = np.exp(-1j * omega * t) gamma_squared = ky**2 + kx**2 result = flat_obj_array( -kx * kz * E_0 * cx * sy * sz * tdep / gamma_squared, # ex -ky * kz * E_0 * sx * cy * sz * tdep / gamma_squared, # ey E_0 * sx * sy * cz * tdep, # ez -1j * omega * ky * E_0 * sx * cy * cz * tdep / gamma_squared, # hx 1j * omega * kx * E_0 * cx * sy * cz * tdep / gamma_squared, zeros, ) return result
def operator(self, t, w): dcoll = self.dcoll u = w[0] v = w[1:] actx = u.array_context # boundary conditions ------------------------------------------------- # dirichlet BCs ------------------------------------------------------- dir_u = op.project(dcoll, "vol", self.dirichlet_tag, u) dir_v = op.project(dcoll, "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 = self.dirichlet_bc_f dir_bc = flat_obj_array(2 * dir_g - dir_u, dir_v) else: dir_bc = flat_obj_array(-dir_u, dir_v) # neumann BCs --------------------------------------------------------- neu_u = op.project(dcoll, "vol", self.neumann_tag, u) neu_v = op.project(dcoll, "vol", self.neumann_tag, v) neu_bc = flat_obj_array(neu_u, -neu_v) # radiation BCs ------------------------------------------------------- rad_normal = thaw(dcoll.normal(dd=self.radiation_tag), actx) rad_u = op.project(dcoll, "vol", self.radiation_tag, u) rad_v = op.project(dcoll, "vol", self.radiation_tag, v) rad_bc = flat_obj_array( 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)) # entire operator ----------------------------------------------------- def flux(tpair): return op.project(dcoll, tpair.dd, "all_faces", self.flux(tpair)) result = (op.inverse_mass( dcoll, flat_obj_array(-self.c * op.weak_local_div(dcoll, v), -self.c * op.weak_local_grad(dcoll, u)) - op.face_mass( dcoll, sum( flux(tpair) for tpair in op.interior_trace_pairs(dcoll, w)) + flux(op.bv_trace_pair(dcoll, self.dirichlet_tag, w, dir_bc)) + flux(op.bv_trace_pair(dcoll, self.neumann_tag, w, neu_bc)) + flux(op.bv_trace_pair(dcoll, self.radiation_tag, w, rad_bc))))) result[0] = result[0] + self.source_f(actx, dcoll, t) return result
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.project("vol", self.dirichlet_tag)(u)) dir_v = sym.cse(sym.project("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.var("dir_bc_u") dir_bc = flat_obj_array(2 * dir_g - dir_u, dir_v) else: dir_bc = flat_obj_array(-dir_u, dir_v) dir_bc = sym.cse(dir_bc, "dir_bc") # neumann BCs --------------------------------------------------------- neu_u = sym.cse(sym.project("vol", self.neumann_tag)(u)) neu_v = sym.cse(sym.project("vol", self.neumann_tag)(v)) neu_bc = sym.cse(flat_obj_array(neu_u, -neu_v), "neu_bc") # radiation BCs ------------------------------------------------------- rad_normal = sym.normal(self.radiation_tag, d) rad_u = sym.cse(sym.project("vol", self.radiation_tag)(u)) rad_v = sym.cse(sym.project("vol", self.radiation_tag)(v)) rad_bc = sym.cse( flat_obj_array( 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.project(pair.dd, "all_faces")(self.flux(pair)) result = sym.InverseMassOperator()(flat_obj_array( -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 __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 flat_obj_array if three_mult is None: return flat_obj_array(*[f(x, y) for f in self.functions]) else: return flat_obj_array( *[sum(three_mult(lc, x[j], y[k]) for lc, j, k in lcjk) for lcjk in self.component_lcjk])
def get_strong_wave_op_with_discr_direct(actx, dims=2, order=4): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dims, b=(0.5, ) * dims, n=(16, ) * dims) logger.debug("%d elements", mesh.nelements) discr = DGDiscretizationWithBoundaries(actx, mesh, order=order) source_center = np.array([0.1, 0.22, 0.33])[:dims] source_width = 0.05 source_omega = 3 sym_x = sym.nodes(mesh.dim) sym_source_center_dist = sym_x - source_center sym_t = sym.ScalarVariable("t") from meshmode.mesh import BTAG_ALL c = -0.1 sign = -1 w = sym.make_sym_array("w", dims + 1) u = w[0] v = w[1:] source_f = ( sym.sin(source_omega * sym_t) * sym.exp(-np.dot(sym_source_center_dist, sym_source_center_dist) / source_width**2)) rad_normal = sym.normal(BTAG_ALL, dims) rad_u = sym.cse(sym.project("vol", BTAG_ALL)(u)) rad_v = sym.cse(sym.project("vol", BTAG_ALL)(v)) rad_bc = sym.cse( flat_obj_array( 0.5 * (rad_u - sign * np.dot(rad_normal, rad_v)), 0.5 * rad_normal * (np.dot(rad_normal, rad_v) - sign * rad_u)), "rad_bc") sym_operator = ( -flat_obj_array(-c * np.dot(sym.nabla(dims), v) - source_f, -c * (sym.nabla(dims) * u)) + sym.InverseMassOperator()( sym.FaceMassOperator() (dg_flux(c, sym.int_tpair(w)) + dg_flux(c, sym.bv_tpair(BTAG_ALL, w, rad_bc))))) return (sym_operator, discr)
def flux(self, w): u = w[0] v = w[1:] normal = sym.normal(w.dd, self.ambient_dim) flux_weak = flat_obj_array(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 * flat_obj_array( 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 dg_flux(c, tpair): u = tpair[0] v = tpair[1:] dims = len(v) normal = sym.normal(tpair.dd, dims) flux_weak = flat_obj_array(np.dot(v.avg, normal), u.avg * normal) flux_weak -= (1 if c > 0 else -1) * flat_obj_array( 0.5 * (u.int - u.ext), 0.5 * (normal * np.dot(normal, v.int - v.ext))) flux_strong = flat_obj_array(np.dot(v.int, normal), u.int * normal) - flux_weak return sym.project(tpair.dd, "all_faces")(c * flux_strong)
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.project("vol", self.absorb_tag)(e)) absorb_h = sym.cse(sym.project("vol", self.absorb_tag)(h)) bc = flat_obj_array( 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 absorbing_bc(self, w): """Construct part of the flux operator template for 1st order absorbing boundary conditions. """ actx = get_container_context_recursively(w) absorb_normal = thaw(self.dcoll.normal(dd=self.absorb_tag), actx) 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 = op.project(self.dcoll, "vol", self.absorb_tag, e) absorb_h = op.project(self.dcoll, "vol", self.absorb_tag, h) bc = flat_obj_array( 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 __call__(self, t, x_vec, eos=IdealSingleGas()): """ Create the lump-of-mass solution at time *t* and locations *x_vec*. Note that *t* is used to advect the mass lump under the assumption of constant, and uniform velocity. Parameters ---------- t: float Current time at which the solution is desired x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.GasEOS` Equation of state class to be used in construction of soln (if needed) """ lump_loc = self._center + t * self._velocity assert len(x_vec) == self._dim # coordinates relative to lump center rel_center = make_obj_array( [x_vec[i] - lump_loc[i] for i in range(self._dim)] ) actx = x_vec[0].array_context r = actx.np.sqrt(np.dot(rel_center, rel_center)) gamma = eos.gamma() expterm = self._rhoamp * actx.np.exp(1 - r ** 2) mass = expterm + self._rho0 mom = self._velocity * mass energy = (self._p0 / (gamma - 1.0)) + np.dot(mom, mom) / (2.0 * mass) return flat_obj_array(mass, energy, mom)
def __call__(self, t, x_vec, eos=IdealSingleGas()): """ Create the isentropic vortex solution at time *t* at locations *x_vec*. Parameters ---------- t: float Current time at which the solution is desired. x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.GasEOS` Equation of state class to be used in construction of soln (if needed) """ vortex_loc = self._center + t * self._velocity # coordinates relative to vortex center x_rel = x_vec[0] - vortex_loc[0] y_rel = x_vec[1] - vortex_loc[1] actx = x_vec[0].array_context gamma = eos.gamma() r = actx.np.sqrt(x_rel ** 2 + y_rel ** 2) expterm = self._beta * actx.np.exp(1 - r ** 2) u = self._velocity[0] - expterm * y_rel / (2 * np.pi) v = self._velocity[1] + expterm * x_rel / (2 * np.pi) mass = (1 - (gamma - 1) / (16 * gamma * np.pi ** 2) * expterm ** 2) ** (1 / (gamma - 1)) p = mass ** gamma e = p / (gamma - 1) + mass / 2 * (u ** 2 + v ** 2) return flat_obj_array(mass, e, mass * u, mass * v)
def get_torus_with_ref_mean_curvature(actx, h): order = 4 r_minor = 1.0 r_major = 3.0 from meshmode.mesh.generation import generate_torus mesh = generate_torus(r_major, r_minor, n_major=h, n_minor=h, order=order) discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) from meshmode.dof_array import thaw nodes = thaw(actx, discr.nodes()) # copied from meshmode.mesh.generation.generate_torus a = r_major b = r_minor u = actx.np.arctan2(nodes[1], nodes[0]) from pytools.obj_array import flat_obj_array rvec = flat_obj_array(actx.np.cos(u), actx.np.sin(u), 0*u) rvec = sum(nodes * rvec) - a cosv = actx.np.cos(actx.np.arctan2(nodes[2], rvec)) return discr, (a + 2.0 * b * cosv) / (2 * b * (a + b * cosv))
def get_example_stepper(actx, dims=2, order=3, use_fusion=True, exec_mapper_factory=ExecutionMapper, return_ic=False): sym_operator, discr = get_wave_op_with_discr( actx, dims=dims, order=3) if not use_fusion: bound_op = bind( discr, sym_operator, exec_mapper_factory=exec_mapper_factory) stepper = RK4TimeStepper( discr, "w", bound_op, 1 + discr.dim, get_wave_component, exec_mapper_factory=exec_mapper_factory) else: stepper = FusedRK4TimeStepper( discr, "w", sym_operator, 1 + discr.dim, get_wave_component, exec_mapper_factory=exec_mapper_factory) if return_ic: ic = flat_obj_array(discr.zeros(actx), [discr.zeros(actx) for i in range(discr.dim)]) return stepper, ic return stepper
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) flattened_results = flat_obj_array(output_t, output_dt, *output_states) self.bound_op = bind( discr, flattened_results, function_registry=function_registry, exec_mapper_factory=exec_mapper_factory)
def sym_wave(dim, sym_phi): """Return symbolic expressions for the wave equation system given a desired solution. (Note: In order to support manufactured solutions, we modify the wave equation to add a source term (f). If the solution is exact, this term should be 0.) """ sym_c = pmbl.var("c") sym_coords = prim.make_sym_vector("x", dim) sym_t = pmbl.var("t") # f = phi_tt - c^2 * div(grad(phi)) sym_f = sym.diff(sym_t)(sym.diff(sym_t)(sym_phi)) - sym_c**2\ * sym.div(sym.grad(dim, sym_phi)) # u = phi_t sym_u = sym.diff(sym_t)(sym_phi) # v = c*grad(phi) sym_v = [sym_c * sym.diff(sym_coords[i])(sym_phi) for i in range(dim)] # rhs(u part) = c*div(v) + f # rhs(v part) = c*grad(u) sym_rhs = flat_obj_array(sym_c * sym.div(sym_v) + sym_f, make_obj_array([sym_c]) * sym.grad(dim, sym_u)) return sym_u, sym_v, sym_f, sym_rhs
def test_stepper_timing(ctx_factory, use_fusion): cl_ctx = ctx_factory() queue = cl.CommandQueue( cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) actx = PyOpenCLArrayContext(queue) dims = 3 sym_operator, discr = get_strong_wave_op_with_discr_direct(actx, dims=dims, order=3) t_start = 0 dt = 0.04 t_end = 0.1 ic = flat_obj_array(discr.zeros(actx), [discr.zeros(actx) for i in range(discr.dim)]) if not use_fusion: bound_op = bind(discr, sym_operator, exec_mapper_factory=ExecutionMapperWithTiming) stepper = RK4TimeStepper(discr, "w", bound_op, 1 + discr.dim, get_strong_wave_component, exec_mapper_factory=ExecutionMapperWithTiming) else: stepper = FusedRK4TimeStepper( discr, "w", sym_operator, 1 + discr.dim, get_strong_wave_component, exec_mapper_factory=ExecutionMapperWithTiming) step = 0 import time t = time.time() 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 tn = time.time() logger.info("step %d/%d: %f", step, nsteps, tn - t) t = tn logger.info("fusion? %s", use_fusion) for key, value in profile_data.items(): if isinstance(value, TimingFutureList): print(key, value.elapsed())
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.project("vol", self.pmc_tag)(e)) pmc_h = sym.cse(sym.project("vol", self.pmc_tag)(h)) return flat_obj_array(pmc_e, -pmc_h)
def flux(self, wtpair): u = wtpair[0] v = wtpair[1:] actx = u.int.array_context normal = thaw(self.dcoll.normal(wtpair.dd), actx) central_flux_weak = -self.c * flat_obj_array(np.dot(v.avg, normal), u.avg * normal) if self.flux_type == "central": return central_flux_weak elif self.flux_type == "upwind": return central_flux_weak - self.c * self.sign * flat_obj_array( 0.5 * (u.ext - u.int), 0.5 * (normal * np.dot(normal, v.ext - v.int))) else: raise ValueError("invalid flux type '%s'" % self.flux_type)
def _bound_op(self, array_context, t, *args, profile_data=None): context = {"t": t, self.field_var_name: flat_obj_array(*args)} result = self.grudge_bound_op(array_context, profile_data=profile_data, **context) if profile_data is not None: result = result[0] return result
def normal(self, where): bdry_discr = self.get_discr(where) ((a, ), (b, )) = parametrization_derivative(self._setup_actx, bdry_discr) nrm = 1 / (a**2 + b**2)**0.5 return freeze(flat_obj_array(b * nrm, -a * nrm))
def test_wave_accuracy(actx_factory, problem, order, visualize=False): """Checks accuracy of the wave operator for a given problem setup. """ actx = actx_factory() p = problem sym_u, sym_v, sym_f, sym_rhs = sym_wave(p.dim, p.sym_phi) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for n in [8, 10, 12] if p.dim == 3 else [8, 12, 16]: mesh = p.mesh_factory(n) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) def sym_eval(expr, t): return sym.EvaluationMapper({"c": p.c, "x": nodes, "t": t})(expr) t_check = 1.23456789 u = sym_eval(sym_u, t_check) v = sym_eval(sym_v, t_check) fields = flat_obj_array(u, v) rhs = wave_operator(discr, c=p.c, w=fields) rhs[0] = rhs[0] + sym_eval(sym_f, t_check) expected_rhs = sym_eval(sym_rhs, t_check) rel_linf_err = actx.to_numpy( discr.norm(rhs - expected_rhs, np.inf) / discr.norm(expected_rhs, np.inf)) eoc_rec.add_data_point(1. / n, rel_linf_err) if visualize: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, discr.order) vis.write_vtk_file( "wave_accuracy_{order}_{n}.vtu".format(order=order, n=n), [ ("u", fields[0]), ("v", fields[1:]), ("rhs_u_actual", rhs[0]), ("rhs_v_actual", rhs[1:]), ("rhs_u_expected", expected_rhs[0]), ("rhs_v_expected", expected_rhs[1:]), ]) print("Approximation error:") print(eoc_rec) assert (eoc_rec.order_estimate() >= order - 0.5 or eoc_rec.max_error() < 1e-11)
def source_f(actx, dcoll, t=0): source_center = np.array([0.1, 0.22, 0.33])[:dcoll.dim] source_width = 0.05 source_omega = 3 nodes = thaw(dcoll.nodes(), actx) source_center_dist = flat_obj_array( [nodes[i] - source_center[i] for i in range(dcoll.dim)]) return (np.sin(source_omega * t) * actx.np.exp( -np.dot(source_center_dist, source_center_dist) / source_width**2))