def check_expansion_disks_undisturbed_by_sources( self, stage1_density_discr, tree, peer_lists, expansion_disturbance_tolerance, refine_flags, debug, wait_for=None): # Avoid generating too many kernels. from pytools import div_ceil max_levels = MAX_LEVELS_INCREMENT * div_ceil(tree.nlevels, MAX_LEVELS_INCREMENT) knl = self.code_container.expansion_disk_undisturbed_by_sources_checker( tree.dimensions, tree.coord_dtype, tree.box_id_dtype, peer_lists.peer_list_starts.dtype, tree.particle_id_dtype, max_levels) if debug: npanels_to_refine_prev = cl.array.sum(refine_flags).get() found_panel_to_refine = cl.array.zeros(self.queue, 1, np.int32) found_panel_to_refine.finish() unwrap_args = AreaQueryElementwiseTemplate.unwrap_args from pytential import bind, sym center_danger_zone_radii = flatten( bind( stage1_density_discr, sym.expansion_radii(stage1_density_discr.ambient_dim, granularity=sym.GRANULARITY_CENTER))( self.array_context)) evt = knl(*unwrap_args( tree, peer_lists, tree.box_to_qbx_source_starts, tree.box_to_qbx_source_lists, tree.qbx_panel_to_source_starts, tree.qbx_panel_to_center_starts, tree.qbx_user_source_slice.start, tree.qbx_user_center_slice.start, tree.sorted_target_ids, center_danger_zone_radii, expansion_disturbance_tolerance, tree.nqbxpanels, refine_flags, found_panel_to_refine, *tree.sources), range=slice(tree.nqbxcenters), queue=self.queue, wait_for=wait_for) cl.wait_for_events([evt]) if debug: npanels_to_refine = cl.array.sum(refine_flags).get() if npanels_to_refine > npanels_to_refine_prev: logger.debug("refiner: found {} panel(s) to refine".format( npanels_to_refine - npanels_to_refine_prev)) return found_panel_to_refine.get()[0] == 1
def map_int_g(self, expr): source_discr = self.places.get_discretization(expr.source.geometry, expr.source.discr_stage) target_discr = self.places.get_discretization(expr.target.geometry, expr.target.discr_stage) if source_discr is not target_discr: raise NotImplementedError rec_density = self._blk_mapper.rec(expr.density) if is_zero(rec_density): return 0 if not np.isscalar(rec_density): raise NotImplementedError # NOTE: copied from pytential.symbolic.primitives.IntG # NOTE: P2P evaluation only uses the inner kernel, so it should not # get other kernel_args, e.g. normal vectors in a double layer kernel = expr.kernel.get_base_kernel() kernel_args = kernel.get_args() + kernel.get_source_args() kernel_args = set(arg.loopy_arg.name for arg in kernel_args) actx = self.array_context kernel_args = _get_layer_potential_args(self._mat_mapper, expr, include_args=kernel_args) if self.exclude_self: kernel_args["target_to_source"] = actx.from_numpy( np.arange(0, target_discr.ndofs, dtype=np.int)) from sumpy.p2p import P2PMatrixBlockGenerator mat_gen = P2PMatrixBlockGenerator(actx.context, (kernel, ), exclude_self=self.exclude_self) from meshmode.dof_array import flatten, thaw _, (mat, ) = mat_gen(actx.queue, targets=flatten(thaw(actx, target_discr.nodes())), sources=flatten(thaw(actx, source_discr.nodes())), index_set=self.index_set, **kernel_args) return rec_density * actx.to_numpy(mat)
def dof_array_to_numpy(actx, ary): """Converts DOFArrays (or object arrays of DOFArrays) to NumPy arrays. Object arrays get turned into multidimensional arrays. """ from pytools.obj_array import obj_array_vectorize from meshmode.dof_array import flatten arr = obj_array_vectorize(actx.to_numpy, flatten(ary)) if arr.dtype.char == "O": arr = np.array(list(arr)) return arr
def my_checkpoint(step, t, dt, state): write_restart = (check_step(step, nrestart) if step != restart_step else False) if write_restart is True: with open(snapshot_pattern.format(step=step, rank=rank), "wb") as f: pickle.dump( { "local_mesh": local_mesh, "state": obj_array_vectorize(actx.to_numpy, flatten(state)), "t": t, "step": step, "global_nelements": global_nelements, "num_parts": nparts, }, f) def loc_fn(t): return flame_start_loc + flame_speed * t exact_soln = PlanarDiscontinuity(dim=dim, disc_location=loc_fn, sigma=0.0000001, nspecies=nspecies, temperature_left=temp_ignition, temperature_right=temp_unburned, pressure_left=pres_burned, pressure_right=pres_unburned, velocity_left=vel_burned, velocity_right=vel_unburned, species_mass_left=y_burned, species_mass_right=y_unburned) cv = split_conserved(dim, state) reaction_rates = eos.get_production_rates(cv) viz_fields = [("reaction_rates", reaction_rates)] return sim_checkpoint(discr=discr, visualizer=visualizer, eos=eos, q=state, vizname=casename, step=step, t=t, dt=dt, nstatus=nstatus, nviz=nviz, exittol=exittol, constant_cfl=constant_cfl, comm=comm, vis_timer=vis_timer, overwrite=True, exact_soln=exact_soln, viz_fields=viz_fields)
def map_int_g(self, expr): source_discr = self.places.get_discretization(expr.source.geometry, expr.source.discr_stage) target_discr = self.places.get_discretization(expr.target.geometry, expr.target.discr_stage) result = 0 for density, kernel in zip(expr.densities, expr.source_kernels): rec_density = self.rec(density) if is_zero(rec_density): continue assert isinstance(rec_density, np.ndarray) if not self.is_kind_matrix(rec_density): raise NotImplementedError("layer potentials on non-variables") # NOTE: copied from pytential.symbolic.primitives.IntG kernel_args = kernel.get_args() + kernel.get_source_args() kernel_args = {arg.loopy_arg.name for arg in kernel_args} actx = self.array_context kernel_args = _get_layer_potential_args(self, expr, include_args=kernel_args) if self.exclude_self: kernel_args["target_to_source"] = actx.from_numpy( np.arange(0, target_discr.ndofs, dtype=np.int64)) from sumpy.p2p import P2PMatrixGenerator mat_gen = P2PMatrixGenerator(actx.context, (kernel, ), exclude_self=self.exclude_self) from meshmode.dof_array import flatten, thaw _, (mat, ) = mat_gen( actx.queue, targets=flatten(thaw(actx, target_discr.nodes())), sources=flatten(thaw(actx, source_discr.nodes())), **kernel_args) result += actx.to_numpy(mat).dot(rec_density) return result
def partition_by_nodes(actx, discr, tree_kind="adaptive-level-restricted", max_nodes_in_box=None): """Generate equally sized ranges of nodes. The partition is created at the lowest level of granularity, i.e. nodes. This results in balanced ranges of points, but will split elements across different ranges. :arg discr: a :class:`meshmode.discretization.Discretization`. :arg tree_kind: if not *None*, it is passed to :class:`boxtree.TreeBuilder`. :arg max_nodes_in_box: passed to :class:`boxtree.TreeBuilder`. :return: a :class:`sumpy.tools.BlockIndexRanges`. """ if max_nodes_in_box is None: # FIXME: this is just an arbitrary value max_nodes_in_box = 32 if tree_kind is not None: from boxtree import box_flags_enum from boxtree import TreeBuilder builder = TreeBuilder(actx.context) from meshmode.dof_array import flatten, thaw tree, _ = builder(actx.queue, flatten(thaw(actx, discr.nodes())), max_particles_in_box=max_nodes_in_box, kind=tree_kind) tree = tree.get(actx.queue) leaf_boxes, = (tree.box_flags & box_flags_enum.HAS_CHILDREN == 0).nonzero() indices = np.empty(len(leaf_boxes), dtype=np.object) for i, ibox in enumerate(leaf_boxes): box_start = tree.box_source_starts[ibox] box_end = box_start + tree.box_source_counts_cumul[ibox] indices[i] = tree.user_source_ids[box_start:box_end] ranges = actx.from_numpy( np.cumsum([0] + [box.shape[0] for box in indices]) ) indices = actx.from_numpy(np.hstack(indices)) else: indices = actx.from_numpy(np.arange(0, discr.ndofs, dtype=np.int)) ranges = actx.from_numpy(np.arange( 0, discr.ndofs + 1, max_nodes_in_box, dtype=np.int)) assert ranges[-1] == discr.ndofs return BlockIndexRanges(actx.context, actx.freeze(indices), actx.freeze(ranges))
def exec_compute_potential_insn_direct(self, actx: PyOpenCLArrayContext, insn, bound_expr, evaluate): kernel_args = {} from pytential.utils import flatten_if_needed from meshmode.dof_array import flatten, thaw, unflatten for arg_name, arg_expr in insn.kernel_arguments.items(): kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr)) from pytential import bind, sym waa = bind(bound_expr.places, sym.weights_and_area_elements( self.ambient_dim, dofdesc=insn.source))(actx) strengths = [waa * evaluate(density) for density in insn.densities] flat_strengths = [flatten(strength) for strength in strengths] results = [] p2p = None for o in insn.outputs: target_discr = bound_expr.places.get_discretization( o.target_name.geometry, o.target_name.discr_stage) if p2p is None: p2p = self.get_p2p(actx, source_kernels=insn.source_kernels, target_kernels=insn.target_kernels) evt, output_for_each_kernel = p2p(actx.queue, flatten_if_needed(actx, target_discr.nodes()), flatten(thaw(actx, self.density_discr.nodes())), flat_strengths, **kernel_args) from meshmode.discretization import Discretization result = output_for_each_kernel[o.target_kernel_index] if isinstance(target_discr, Discretization): result = unflatten(actx, target_discr, result) results.append((o.name, result)) timing_data = {} return results, timing_data
def interpolate_to_meshmode(queue, potential, leaves_to_nodes_lookup, order="tree"): """ :arg potential: a DoF vector representing a field in :mod:`volumential`, in tree order. :arg leaves_to_nodes_lookup: a :class:`LeavesToNodesLookup`. :arg order: order of the input potential, either "tree" or "user". :returns: a :class:`pyopencl.Array` of shape (nnodes, 1) containing the interpolated data. """ if order == "tree": potential_in_tree_order = True elif order == "user": potential_in_tree_order = False else: raise ValueError(f"order must be 'tree' or 'user' (got {order}).") arr_ctx = PyOpenCLArrayContext(queue) target_points = flatten(thaw(arr_ctx, leaves_to_nodes_lookup.discr.nodes())) traversal = leaves_to_nodes_lookup.trav tree = leaves_to_nodes_lookup.trav.tree dim = tree.dimensions # infer q_order from tree pts_per_box = tree.ntargets // traversal.ntarget_boxes assert pts_per_box * traversal.ntarget_boxes == tree.ntargets # allow for +/- 0.25 floating point error q_order = int(pts_per_box**(1 / dim) + 0.25) assert q_order**dim == pts_per_box interp_p = interpolate_volume_potential( target_points=target_points, traversal=traversal, wrangler=None, potential=potential, potential_in_tree_order=potential_in_tree_order, dim=dim, tree=tree, queue=queue, q_order=q_order, dtype=potential.dtype, lbl_lookup=None, balls_near_box_starts=leaves_to_nodes_lookup.nodes_in_leaf_starts, balls_near_box_lists=leaves_to_nodes_lookup.nodes_in_leaf_lists) return interp_p
def test_array_context_np_workalike(actx_factory): actx = actx_factory() from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh( a=(-0.5,)*2, b=(0.5,)*2, n=(8,)*2, order=3) discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3)) for sym_name, n_args in [ ("sin", 1), ("exp", 1), ("arctan2", 2), ("minimum", 2), ("maximum", 2), ("where", 3), ("conj", 1), ]: args = [np.random.randn(discr.ndofs) for i in range(n_args)] ref_result = getattr(np, sym_name)(*args) # {{{ test DOFArrays actx_args = [unflatten(actx, discr, actx.from_numpy(arg)) for arg in args] actx_result = actx.to_numpy( flatten(getattr(actx.np, sym_name)(*actx_args))) assert np.allclose(actx_result, ref_result) # }}} # {{{ test object arrays of DOFArrays obj_array_args = [make_obj_array([arg]) for arg in actx_args] obj_array_result = actx.to_numpy( flatten(getattr(actx.np, sym_name)(*obj_array_args)[0])) assert np.allclose(obj_array_result, ref_result)
def check_sufficient_source_quadrature_resolution(self, stage2_density_discr, tree, peer_lists, refine_flags, debug, wait_for=None): # Avoid generating too many kernels. from pytools import div_ceil max_levels = MAX_LEVELS_INCREMENT * div_ceil(tree.nlevels, MAX_LEVELS_INCREMENT) knl = self.code_container.sufficient_source_quadrature_resolution_checker( tree.dimensions, tree.coord_dtype, tree.box_id_dtype, peer_lists.peer_list_starts.dtype, tree.particle_id_dtype, max_levels) if debug: npanels_to_refine_prev = cl.array.sum(refine_flags).get() found_panel_to_refine = cl.array.zeros(self.queue, 1, np.int32) found_panel_to_refine.finish() from pytential import bind, sym dd = sym.as_dofdesc(sym.GRANULARITY_ELEMENT).to_stage2() source_danger_zone_radii_by_panel = flatten( bind( stage2_density_discr, sym._source_danger_zone_radii(stage2_density_discr.ambient_dim, dofdesc=dd))(self.array_context)) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl(*unwrap_args( tree, peer_lists, tree.box_to_qbx_center_starts, tree.box_to_qbx_center_lists, tree.qbx_panel_to_source_starts, tree.qbx_user_source_slice.start, tree.qbx_user_center_slice.start, tree.sorted_target_ids, source_danger_zone_radii_by_panel, tree.nqbxpanels, refine_flags, found_panel_to_refine, *tree.sources), range=slice(tree.nqbxsources), queue=self.queue, wait_for=wait_for) cl.wait_for_events([evt]) if debug: npanels_to_refine = cl.array.sum(refine_flags).get() if npanels_to_refine > npanels_to_refine_prev: logger.debug("refiner: found {} panel(s) to refine".format( npanels_to_refine - npanels_to_refine_prev)) return found_panel_to_refine.get()[0] == 1
def flat_centers(self): """Return an object array of (interleaved) center coordinates. ``coord_t [ambient_dim][ncenters]`` """ from pytential import bind, sym centers = bind( self.places, sym.interleaved_expansion_centers( self.ambient_dim, dofdesc=self.source_dd.to_stage1()))(self.array_context) return obj_array_vectorize(self.array_context.freeze, flatten(centers))
def test_tangential_onb(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from meshmode.mesh.generation import generate_torus mesh = generate_torus(5, 2, order=3) discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(3)) tob = sym.tangential_onb(mesh.ambient_dim) nvecs = tob.shape[1] # make sure tangential_onb is mutually orthogonal and normalized orth_check = bind( discr, sym.make_obj_array([ np.dot(tob[:, i], tob[:, j]) - (1 if i == j else 0) for i in range(nvecs) for j in range(nvecs) ]))(actx) from meshmode.dof_array import flatten orth_check = flatten(orth_check) for i, orth_i in enumerate(orth_check): assert (cl.clmath.fabs(orth_i) < 1e-13).get().all() # make sure tangential_onb is orthogonal to normal orth_check = bind( discr, sym.make_obj_array([ np.dot(tob[:, i], sym.normal(mesh.ambient_dim).as_vector()) for i in range(nvecs) ]))(actx) orth_check = flatten(orth_check) for i, orth_i in enumerate(orth_check): assert (cl.clmath.fabs(orth_i) < 1e-13).get().all()
def my_checkpoint(step, t, dt, state): write_restart = (check_step(step, nrestart) if step != restart_step else False) if write_restart is True: with open(snapshot_pattern.format(step=step, rank=rank), "wb") as f: pickle.dump( { "local_mesh": local_mesh, "state": obj_array_vectorize(actx.to_numpy, flatten(state)), "t": t, "step": step, "global_nelements": global_nelements, "num_parts": nparts, }, f) uc = np.zeros(shape=(dim, )) uc[0] = mach * c_bkrnd #x0=f(time) exact_soln = Discontinuity(dim=dim, x0=.05, sigma=0.00001, rhol=rho2, rhor=rho1, pl=pressure2, pr=pressure1, ul=velocity2, ur=velocity1, uc=uc) return sim_checkpoint(discr=discr, visualizer=visualizer, eos=eos, q=state, vizname=casename, step=step, t=t, dt=dt, nstatus=nstatus, nviz=nviz, exittol=exittol, constant_cfl=constant_cfl, comm=comm, vis_timer=vis_timer, overwrite=True, exact_soln=exact_soln, sigma=sigma_sc, kappa=kappa_sc)
def resample_to_numpy(conn, vec): if (isinstance(vec, np.ndarray) and vec.dtype.char == "O" and not isinstance(vec, DOFArray)): from pytools.obj_array import obj_array_vectorize return obj_array_vectorize(lambda x: resample_to_numpy(conn, x), vec) from numbers import Number if isinstance(vec, Number): nnodes = sum(grp.ndofs for grp in conn.to_discr.groups) return np.ones(nnodes) * vec else: resampled = conn(vec) actx = resampled.array_context return actx.to_numpy(flatten(resampled))
def flatten_if_needed(actx: PyOpenCLArrayContext, ary: np.ndarray): from pytools.obj_array import obj_array_vectorize_n_args from meshmode.dof_array import DOFArray, thaw, flatten if (isinstance(ary, np.ndarray) and ary.dtype.char == "O" and not isinstance(ary, DOFArray)): return obj_array_vectorize_n_args(flatten_if_needed, actx, ary) if not isinstance(ary, DOFArray): return ary if ary.array_context is None: ary = thaw(actx, ary) return flatten(ary)
def flat_expansion_radii(self): """Return an array of radii associated with the (interleaved) expansion centers. ``coord_t [ncenters]`` """ from pytential import bind, sym radii = bind( self.places, sym.expansion_radii(self.ambient_dim, granularity=sym.GRANULARITY_CENTER, dofdesc=self.source_dd.to_stage1()))( self.array_context) return self.array_context.freeze(flatten(radii))
def test_unregularized_with_ones_kernel(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nelements = 10 order = 8 mesh = make_curve_mesh(partial(ellipse, 1), np.linspace(0, 1, nelements+1), order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) from pytential.unregularized import UnregularizedLayerPotentialSource lpot_source = UnregularizedLayerPotentialSource(discr) from pytential.target import PointsTarget targets = PointsTarget(np.zeros((2, 1), dtype=float)) places = GeometryCollection({ sym.DEFAULT_SOURCE: lpot_source, sym.DEFAULT_TARGET: lpot_source, "target_non_self": targets}) from sumpy.kernel import one_kernel_2d sigma_sym = sym.var("sigma") op = sym.IntG(one_kernel_2d, sigma_sym, qbx_forced_limit=None) sigma = discr.zeros(actx) + 1 result_self = bind(places, op, auto_where=places.auto_where)( actx, sigma=sigma) result_nonself = bind(places, op, auto_where=(places.auto_source, "target_non_self"))( actx, sigma=sigma) from meshmode.dof_array import flatten assert np.allclose(actx.to_numpy(flatten(result_self)), 2 * np.pi) assert np.allclose(actx.to_numpy(result_nonself), 2 * np.pi)
def __call__(self, queue, tol=1e-12, wait_for=None): """ :arg queue: a :class:`pyopencl.CommandQueue` :tol: nodes close enough to the boundary will be treated as lying on the boundary, whose interpolated values are averaged. """ arr_ctx = PyOpenCLArrayContext(queue) nodes = flatten(thaw(arr_ctx, self.discr.nodes())) radii = cl.array.zeros_like(nodes[0]) + tol lbl_lookup, evt = self.leaves_to_balls_lookup_builder( queue, self.trav.tree, nodes, radii, wait_for=wait_for) return LeavesToNodesLookup( trav=self.trav, discr=self.discr, nodes_in_leaf_starts=lbl_lookup.balls_near_box_starts, nodes_in_leaf_lists=lbl_lookup.balls_near_box_lists), evt
def resample_to_numpy(conn, vec, *, stack=False, by_group=False): """ :arg stack: if *True* object arrays are stacked into a single :class:`~numpy.ndarray`. :arg by_group: if *True*, the per-group arrays in a :class:`DOFArray` are flattened separately. This can be used to write each group as a separate mesh (in supporting formats). """ # "stack" exists as mainly as a workaround for Xdmf. See here: # https://github.com/inducer/pyvisfile/pull/12#discussion_r550959081 # for (minimal) discussion. if isinstance(vec, np.ndarray) and vec.dtype.char == "O": from pytools.obj_array import obj_array_vectorize r = obj_array_vectorize( lambda x: resample_to_numpy(conn, x, by_group=by_group), vec) return _stack_object_array(r, by_group=by_group) if stack else r if isinstance(vec, DOFArray): actx = vec.array_context vec = conn(vec) from numbers import Number if by_group: if isinstance(vec, Number): return make_obj_array([ np.full(grp.ndofs, vec) for grp in conn.to_discr.groups ]) elif isinstance(vec, DOFArray): return make_obj_array([ actx.to_numpy(ivec).reshape(-1) for ivec in vec ]) else: raise TypeError(f"unsupported array type: {type(vec).__name__}") else: if isinstance(vec, Number): nnodes = sum(grp.ndofs for grp in conn.to_discr.groups) return np.full(nnodes, vec) elif isinstance(vec, DOFArray): return actx.to_numpy(flatten(vec)) else: raise TypeError(f"unsupported array type: {type(vec).__name__}")
def map_insn_rank_data_swap(self, insn, profile_data=None): local_data = self.array_context.to_numpy(flatten(self.rec(insn.field))) comm = self.discrwb.mpi_communicator # print("Sending data to rank %d with tag %d" # % (insn.i_remote_rank, insn.send_tag)) send_req = comm.Isend(local_data, insn.i_remote_rank, tag=insn.send_tag) remote_data_host = np.empty_like(local_data) recv_req = comm.Irecv(remote_data_host, insn.i_remote_rank, insn.recv_tag) return [], [ MPIRecvFuture( array_context=self.array_context, bdry_discr=self.discrwb.discr_from_dd(insn.dd_out), recv_req=recv_req, insn_name=insn.name, remote_data_host=remote_data_host), MPISendFuture(send_req)]
def __init__(self, actx, discr, order, visualize=True, ylim=None): self.actx = actx self.dim = discr.ambient_dim self.visualize = visualize if not self.visualize: return if self.dim == 1: import matplotlib.pyplot as pt self.fig = pt.figure(figsize=(8, 8), dpi=300) self.ylim = ylim volume_discr = discr.discr_from_dd(dof_desc.DD_VOLUME) self.x = actx.to_numpy(flatten(thaw(actx, volume_discr.nodes()[0]))) else: from grudge.shortcuts import make_visualizer self.vis = make_visualizer(discr)
def __init__(self, actx, discr, order, visualize=True): self.actx = actx self.ambient_dim = discr.ambient_dim self.dim = discr.dim self.visualize = visualize if not self.visualize: return if self.ambient_dim == 2: import matplotlib.pyplot as pt self.fig = pt.figure(figsize=(8, 8), dpi=300) x = thaw(actx, discr.discr_from_dd(dof_desc.DD_VOLUME).nodes()) self.x = actx.to_numpy(flatten(actx.np.atan2(x[1], x[0]))) elif self.ambient_dim == 3: from grudge.shortcuts import make_visualizer self.vis = make_visualizer(discr) else: raise ValueError("unsupported dimension")
def __init__(self, discrwb, remote_rank, vol_field, tag=None): self.tag = self.base_tag if tag is not None: self.tag += tag self.discrwb = discrwb self.array_context = vol_field.array_context self.remote_btag = BTAG_PARTITION(remote_rank) self.bdry_discr = discrwb.discr_from_dd(self.remote_btag) self.local_dof_array = discrwb.project("vol", self.remote_btag, vol_field) local_data = self.array_context.to_numpy(flatten(self.local_dof_array)) comm = self.discrwb.mpi_communicator self.send_req = comm.Isend( local_data, remote_rank, tag=self.tag) self.remote_data_host = np.empty_like(local_data) self.recv_req = comm.Irecv(self.remote_data_host, remote_rank, self.tag)
def my_checkpoint(step, t, dt, state): write_restart = (check_step(step, nrestart) if step != restart_step else False) if write_restart is True: with open(snapshot_pattern.format(step=step, rank=rank), "wb") as f: pickle.dump({ "local_mesh": local_mesh, "state": obj_array_vectorize(actx.to_numpy, flatten(state)), "t": t, "step": step, "global_nelements": global_nelements, "num_parts": nparts, }, f) return sim_checkpoint(discr=discr, visualizer=visualizer, eos=eos, q=state, vizname=casename, step=step, t=t, dt=dt, nstatus=nstatus, nviz=nviz, exittol=exittol, constant_cfl=constant_cfl, comm=comm, vis_timer=vis_timer, overwrite=True,s0=s0_sc,kappa=kappa_sc)
def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, return_timing_data): from pytential import bind, sym if return_timing_data: from pytential.source import UnableToCollectTimingData from warnings import warn warn("Timing data collection not supported.", category=UnableToCollectTimingData) lpot_applier = self.get_lpot_applier(insn.target_kernels, insn.source_kernels) p2p = None lpot_applier_on_tgt_subset = None from pytential.utils import flatten_if_needed kernel_args = {} for arg_name, arg_expr in insn.kernel_arguments.items(): kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr)) waa = bind( bound_expr.places, sym.weights_and_area_elements(self.ambient_dim, dofdesc=insn.source))(actx) strength_vecs = [waa * evaluate(density) for density in insn.densities] from meshmode.discretization import Discretization flat_strengths = [flatten(strength) for strength in strength_vecs] source_discr = bound_expr.places.get_discretization( insn.source.geometry, insn.source.discr_stage) # FIXME: Do this all at once results = [] for o in insn.outputs: source_dd = insn.source.copy(discr_stage=o.target_name.discr_stage) target_discr = bound_expr.places.get_discretization( o.target_name.geometry, o.target_name.discr_stage) density_discr = bound_expr.places.get_discretization( source_dd.geometry, source_dd.discr_stage) is_self = density_discr is target_discr if is_self: # QBXPreprocessor is supposed to have taken care of this assert o.qbx_forced_limit is not None assert abs(o.qbx_forced_limit) > 0 expansion_radii = bind( bound_expr.places, sym.expansion_radii(self.ambient_dim, dofdesc=o.target_name))(actx) centers = bind( bound_expr.places, sym.expansion_centers(self.ambient_dim, o.qbx_forced_limit, dofdesc=o.target_name))(actx) evt, output_for_each_kernel = lpot_applier( actx.queue, flatten(thaw(actx, target_discr.nodes())), flatten(thaw(actx, source_discr.nodes())), flatten(centers), flat_strengths, expansion_radii=flatten(expansion_radii), **kernel_args) result = output_for_each_kernel[o.target_kernel_index] if isinstance(target_discr, Discretization): result = unflatten(actx, target_discr, result) results.append((o.name, result)) else: # no on-disk kernel caching if p2p is None: p2p = self.get_p2p(actx, insn.target_kernels, insn.source_kernels) if lpot_applier_on_tgt_subset is None: lpot_applier_on_tgt_subset = self.get_lpot_applier_on_tgt_subset( insn.target_kernels, insn.source_kernels) queue = actx.queue flat_targets = flatten_if_needed(actx, target_discr.nodes()) flat_sources = flatten(thaw(actx, source_discr.nodes())) evt, output_for_each_kernel = p2p(queue, flat_targets, flat_sources, flat_strengths, **kernel_args) qbx_forced_limit = o.qbx_forced_limit if qbx_forced_limit is None: qbx_forced_limit = 0 target_discrs_and_qbx_sides = ((target_discr, qbx_forced_limit), ) geo_data = self.qbx_fmm_geometry_data( bound_expr.places, insn.source.geometry, target_discrs_and_qbx_sides=target_discrs_and_qbx_sides) # center-related info is independent of targets # First ncenters targets are the centers tgt_to_qbx_center = ( geo_data.user_target_to_center()[geo_data.ncenters:].copy( queue=queue).with_queue(queue)) qbx_tgt_numberer = self.get_qbx_target_numberer( tgt_to_qbx_center.dtype) qbx_tgt_count = cl.array.empty(queue, (), np.int32) qbx_tgt_numbers = cl.array.empty_like(tgt_to_qbx_center) qbx_tgt_numberer(tgt_to_qbx_center, qbx_tgt_numbers, qbx_tgt_count, queue=queue) qbx_tgt_count = int(qbx_tgt_count.get()) if (o.qbx_forced_limit is not None and abs(o.qbx_forced_limit) == 1 and qbx_tgt_count < target_discr.ndofs): raise RuntimeError("Did not find a matching QBX center " "for some targets") qbx_tgt_numbers = qbx_tgt_numbers[:qbx_tgt_count] qbx_center_numbers = tgt_to_qbx_center[qbx_tgt_numbers] qbx_center_numbers.finish() tgt_subset_kwargs = kernel_args.copy() for i, res_i in enumerate(output_for_each_kernel): tgt_subset_kwargs[f"result_{i}"] = res_i if qbx_tgt_count: lpot_applier_on_tgt_subset( queue, targets=flat_targets, sources=flat_sources, centers=geo_data.flat_centers(), expansion_radii=geo_data.flat_expansion_radii(), strengths=flat_strengths, qbx_tgt_numbers=qbx_tgt_numbers, qbx_center_numbers=qbx_center_numbers, **tgt_subset_kwargs) result = output_for_each_kernel[o.target_kernel_index] if isinstance(target_discr, Discretization): result = unflatten(actx, target_discr, result) results.append((o.name, result)) timing_data = {} return results, timing_data
def exec_compute_potential_insn_fmm(self, actx: PyOpenCLArrayContext, insn, bound_expr, evaluate, fmm_driver): """ :arg fmm_driver: A function that accepts four arguments: *wrangler*, *strength*, *geo_data*, *kernel*, *kernel_arguments* :returns: a tuple ``(assignments, extra_outputs)``, where *assignments* is a list of tuples containing pairs ``(name, value)`` representing assignments to be performed in the evaluation context. *extra_outputs* is data that *fmm_driver* may return (such as timing data), passed through unmodified. """ target_name_and_side_to_number, target_discrs_and_qbx_sides = ( self.get_target_discrs_and_qbx_sides(insn, bound_expr)) geo_data = self.qbx_fmm_geometry_data(bound_expr.places, insn.source.geometry, target_discrs_and_qbx_sides) # FIXME Exert more positive control over geo_data attribute lifetimes using # geo_data.<method>.clear_cache(geo_data). # FIXME Synthesize "bad centers" around corners and edges that have # inadequate QBX coverage. # FIXME don't compute *all* output kernels on all targets--respect that # some target discretizations may only be asking for derivatives (e.g.) from pytential import bind, sym waa = bind( bound_expr.places, sym.weights_and_area_elements(self.ambient_dim, dofdesc=insn.source))(actx) densities = [evaluate(density) for density in insn.densities] strengths = [waa * density for density in densities] flat_strengths = tuple(flatten(strength) for strength in strengths) base_kernel = single_valued(knl.get_base_kernel() for knl in insn.source_kernels) output_and_expansion_dtype = (self.get_fmm_output_and_expansion_dtype( insn.source_kernels, flat_strengths[0])) kernel_extra_kwargs, source_extra_kwargs = ( self.get_fmm_expansion_wrangler_extra_kwargs( actx, insn.target_kernels + insn.source_kernels, geo_data.tree().user_source_ids, insn.kernel_arguments, evaluate)) wrangler = self.expansion_wrangler_code_container( target_kernels=insn.target_kernels, source_kernels=insn.source_kernels).get_wrangler( actx.queue, geo_data, output_and_expansion_dtype, self.qbx_order, self.fmm_level_to_order, source_extra_kwargs=source_extra_kwargs, kernel_extra_kwargs=kernel_extra_kwargs, _use_target_specific_qbx=self._use_target_specific_qbx) from pytential.qbx.geometry import target_state if (actx.thaw(geo_data.user_target_to_center()) == target_state.FAILED ).any().get(): raise RuntimeError("geometry has failed targets") # {{{ geometry data inspection hook if self.geometry_data_inspector is not None: perform_fmm = self.geometry_data_inspector(insn, bound_expr, geo_data) if not perform_fmm: return [(o.name, 0) for o in insn.outputs] # }}} # Execute global QBX. all_potentials_on_every_target, extra_outputs = (fmm_driver( wrangler, flat_strengths, geo_data, base_kernel, kernel_extra_kwargs)) results = [] for o in insn.outputs: target_side_number = target_name_and_side_to_number[ o.target_name, o.qbx_forced_limit] target_discr, _ = target_discrs_and_qbx_sides[target_side_number] target_slice = slice(*geo_data.target_info( ).target_discr_starts[target_side_number:target_side_number + 2]) result = \ all_potentials_on_every_target[o.target_kernel_index][target_slice] from meshmode.discretization import Discretization if isinstance(target_discr, Discretization): from meshmode.dof_array import unflatten result = unflatten(actx, target_discr, result) results.append((o.name, result)) return results, extra_outputs
def _test_data_transfer(mpi_comm, actx, local_bdry_conns, remote_to_local_bdry_conns, connected_parts): from mpi4py import MPI def f(x): return 10 * actx.np.sin(20. * x) """ Here is a simplified example of what happens from the point of view of the local rank. Local rank: 1. Transfer local points from local boundary to remote boundary to get remote points. 2. Send remote points to remote rank. Remote rank: 3. Receive remote points from local rank. 4. Transfer remote points from remote boundary to local boundary to get local points. 5. Send local points to local rank. Local rank: 6. Receive local points from remote rank. 7. Check if local points are the same as the original local points. """ # 1. send_reqs = [] for i_remote_part in connected_parts: conn = remote_to_local_bdry_conns[i_remote_part] bdry_discr = local_bdry_conns[i_remote_part].to_discr bdry_x = thaw(actx, bdry_discr.nodes()[0]) true_local_f = f(bdry_x) remote_f = conn(true_local_f) # 2. send_reqs.append( mpi_comm.isend(actx.to_numpy(flatten(remote_f)), dest=i_remote_part, tag=TAG_SEND_REMOTE_NODES)) # 3. buffers = {} for i_remote_part in connected_parts: status = MPI.Status() mpi_comm.probe(source=i_remote_part, tag=TAG_SEND_REMOTE_NODES, status=status) buffers[i_remote_part] = np.empty(status.count, dtype=bytes) recv_reqs = {} for i_remote_part, buf in buffers.items(): recv_reqs[i_remote_part] = mpi_comm.irecv(buf=buf, source=i_remote_part, tag=TAG_SEND_REMOTE_NODES) remote_to_local_f_data = {} for i_remote_part, req in recv_reqs.items(): remote_to_local_f_data[i_remote_part] = req.wait() buffers[i_remote_part] = None # free buffer for req in send_reqs: req.wait() # 4. send_reqs = [] for i_remote_part in connected_parts: conn = remote_to_local_bdry_conns[i_remote_part] local_f = unflatten( actx, conn.from_discr, actx.from_numpy(remote_to_local_f_data[i_remote_part])) remote_f = actx.to_numpy(flatten(conn(local_f))) # 5. send_reqs.append( mpi_comm.isend(remote_f, dest=i_remote_part, tag=TAG_SEND_LOCAL_NODES)) # 6. buffers = {} for i_remote_part in connected_parts: status = MPI.Status() mpi_comm.probe(source=i_remote_part, tag=TAG_SEND_LOCAL_NODES, status=status) buffers[i_remote_part] = np.empty(status.count, dtype=bytes) recv_reqs = {} for i_remote_part, buf in buffers.items(): recv_reqs[i_remote_part] = mpi_comm.irecv(buf=buf, source=i_remote_part, tag=TAG_SEND_LOCAL_NODES) local_f_data = {} for i_remote_part, req in recv_reqs.items(): local_f_data[i_remote_part] = req.wait() buffers[i_remote_part] = None # free buffer for req in send_reqs: req.wait() # 7. for i_remote_part in connected_parts: bdry_discr = local_bdry_conns[i_remote_part].to_discr bdry_x = thaw(actx, bdry_discr.nodes()[0]) true_local_f = actx.to_numpy(flatten(f(bdry_x))) local_f = local_f_data[i_remote_part] from numpy.linalg import norm err = norm(true_local_f - local_f, np.inf) assert err < 1e-11, "Error = %f is too large" % err
def build_tree_with_qbx_metadata(actx: PyOpenCLArrayContext, places, tree_builder, particle_list_filter, sources_list=(), targets_list=(), use_stage2_discr=False): """Return a :class:`TreeWithQBXMetadata` built from the given layer potential source. This contains particles of four different types: * source particles either from :class:`~pytential.symbolic.primitives.QBX_SOURCE_STAGE1` or :class:`~pytential.symbolic.primitives.QBX_SOURCE_QUAD_STAGE2`. * centers from :class:`~pytential.symbolic.primitives.QBX_SOURCE_STAGE1`. * targets from ``targets_list``. :arg actx: A :class:`PyOpenCLArrayContext` :arg places: An instance of :class:`~pytential.symbolic.execution.GeometryCollection`. :arg targets_list: A list of :class:`pytential.target.TargetBase` :arg use_stage2_discr: If *True*, builds a tree with stage 2 sources. If *False*, the tree is built with stage 1 sources. """ # The ordering of particles is as follows: # - sources go first # - then centers # - then targets from pytential import bind, sym stage1_density_discrs = [] density_discrs = [] for source_name in sources_list: dd = sym.as_dofdesc(source_name) discr = places.get_discretization(dd.geometry) stage1_density_discrs.append(discr) if use_stage2_discr: discr = places.get_discretization(dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) density_discrs.append(discr) # TODO: update code to work for multiple source discretizations if len(sources_list) != 1: raise RuntimeError("can only build a tree for a single source") def _make_centers(discr): return bind(discr, sym.interleaved_expansion_centers(discr.ambient_dim))(actx) stage1_density_discr = stage1_density_discrs[0] density_discr = density_discrs[0] from meshmode.dof_array import flatten, thaw from pytential.utils import flatten_if_needed sources = flatten(thaw(actx, density_discr.nodes())) centers = flatten(_make_centers(stage1_density_discr)) targets = [flatten_if_needed(actx, tgt.nodes()) for tgt in targets_list] queue = actx.queue particles = tuple( cl.array.concatenate(dim_coords, queue=queue) for dim_coords in zip(sources, centers, *targets)) # Counts nparticles = len(particles[0]) npanels = density_discr.mesh.nelements nsources = len(sources[0]) ncenters = len(centers[0]) # Each source gets an interior / exterior center. assert 2 * nsources == ncenters or use_stage2_discr ntargets = sum(tgt.ndofs for tgt in targets_list) # Slices qbx_user_source_slice = slice(0, nsources) center_slice_start = nsources qbx_user_center_slice = slice(center_slice_start, center_slice_start + ncenters) panel_slice_start = center_slice_start + ncenters target_slice_start = panel_slice_start qbx_user_target_slice = slice(target_slice_start, target_slice_start + ntargets) # Build tree with sources and centers. Split boxes # only because of sources. refine_weights = cl.array.zeros(queue, nparticles, np.int32) refine_weights[:nsources].fill(1) refine_weights.finish() tree, evt = tree_builder(queue, particles, max_leaf_refine_weight=MAX_REFINE_WEIGHT, refine_weights=refine_weights) # Compute box => particle class relations flags = refine_weights del refine_weights particle_classes = {} for class_name, particle_slice, fixup in (("box_to_qbx_source", qbx_user_source_slice, 0), ("box_to_qbx_target", qbx_user_target_slice, -target_slice_start), ("box_to_qbx_center", qbx_user_center_slice, -center_slice_start)): flags.fill(0) flags[particle_slice].fill(1) flags.finish() box_to_class = (particle_list_filter.filter_target_lists_in_user_order( queue, tree, flags).with_queue(actx.queue)) if fixup: box_to_class.target_lists += fixup particle_classes[class_name + "_starts"] = box_to_class.target_starts particle_classes[class_name + "_lists"] = box_to_class.target_lists del flags del box_to_class # Compute panel => source relation qbx_panel_to_source_starts = cl.array.empty(queue, npanels + 1, dtype=tree.particle_id_dtype) el_offset = 0 node_nr_base = 0 for group in density_discr.groups: qbx_panel_to_source_starts[el_offset:el_offset + group.nelements] = \ cl.array.arange(queue, node_nr_base, node_nr_base + group.ndofs, group.nunit_dofs, dtype=tree.particle_id_dtype) node_nr_base += group.ndofs el_offset += group.nelements qbx_panel_to_source_starts[-1] = nsources # Compute panel => center relation qbx_panel_to_center_starts = (2 * qbx_panel_to_source_starts if not use_stage2_discr else None) # Transfer all tree attributes. tree_attrs = {} for attr_name in tree.__class__.fields: try: tree_attrs[attr_name] = getattr(tree, attr_name) except AttributeError: pass tree_attrs.update(particle_classes) return TreeWithQBXMetadata( qbx_panel_to_source_starts=qbx_panel_to_source_starts, qbx_panel_to_center_starts=qbx_panel_to_center_starts, qbx_user_source_slice=qbx_user_source_slice, qbx_user_center_slice=qbx_user_center_slice, qbx_user_target_slice=qbx_user_target_slice, nqbxpanels=npanels, nqbxsources=nsources, nqbxcenters=ncenters, nqbxtargets=ntargets, **tree_attrs).with_queue(None)
def test_mass_mat_trig(ctx_factory, ambient_dim, quad_tag): """Check the integral of some trig functions on an interval using the mass matrix. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nelements = 17 order = 4 a = -4.0 * np.pi b = +9.0 * np.pi true_integral = 13 * np.pi / 2 * (b - a)**(ambient_dim - 1) from meshmode.discretization.poly_element import QuadratureSimplexGroupFactory dd_quad = sym.DOFDesc(sym.DTAG_VOLUME_ALL, quad_tag) if quad_tag is sym.QTAG_NONE: quad_tag_to_group_factory = {} else: quad_tag_to_group_factory = { quad_tag: QuadratureSimplexGroupFactory(order=2 * order) } from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(a, ) * ambient_dim, b=(b, ) * ambient_dim, n=(nelements, ) * ambient_dim, order=1) discr = DGDiscretizationWithBoundaries( actx, mesh, order=order, quad_tag_to_group_factory=quad_tag_to_group_factory) def _get_variables_on(dd): sym_f = sym.var("f", dd=dd) sym_x = sym.nodes(ambient_dim, dd=dd) sym_ones = sym.Ones(dd) return sym_f, sym_x, sym_ones sym_f, sym_x, sym_ones = _get_variables_on(sym.DD_VOLUME) f_volm = actx.to_numpy(flatten(bind(discr, sym.cos(sym_x[0])**2)(actx))) ones_volm = actx.to_numpy(flatten(bind(discr, sym_ones)(actx))) sym_f, sym_x, sym_ones = _get_variables_on(dd_quad) f_quad = bind(discr, sym.cos(sym_x[0])**2)(actx) ones_quad = bind(discr, sym_ones)(actx) mass_op = bind(discr, sym.MassOperator(dd_quad, sym.DD_VOLUME)(sym_f)) num_integral_1 = np.dot(ones_volm, actx.to_numpy(flatten(mass_op(f=f_quad)))) err_1 = abs(num_integral_1 - true_integral) assert err_1 < 1e-9, err_1 num_integral_2 = np.dot(f_volm, actx.to_numpy(flatten(mass_op(f=ones_quad)))) err_2 = abs(num_integral_2 - true_integral) assert err_2 < 1.0e-9, err_2 if quad_tag is sym.QTAG_NONE: # NOTE: `integral` always makes a square mass matrix and # `QuadratureSimplexGroupFactory` does not have a `mass_matrix` method. num_integral_3 = bind(discr, sym.integral(sym_f, dd=dd_quad))(f=f_quad) err_3 = abs(num_integral_3 - true_integral) assert err_3 < 5.0e-10, err_3
def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, force_direct, visualize=False): logging.basicConfig(level=logging.INFO) print("ellipse_aspect: %s, mode_nr: %d, qbx_order: %d" % (ellipse_aspect, mode_nr, qbx_order)) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 8 from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource from pytools.convergence import EOCRecorder s_eoc_rec = EOCRecorder() d_eoc_rec = EOCRecorder() sp_eoc_rec = EOCRecorder() if ellipse_aspect != 1: nelements_values = [60, 100, 150, 200] else: nelements_values = [30, 70] # See # # [1] G. J. Rodin and O. Steinbach, "Boundary Element Preconditioners # for Problems Defined on Slender Domains", SIAM Journal on Scientific # Computing, Vol. 24, No. 4, pg. 1450, 2003. # https://dx.doi.org/10.1137/S1064827500372067 for nelements in nelements_values: mesh = make_curve_mesh(partial(ellipse, ellipse_aspect), np.linspace(0, 1, nelements + 1), target_order) fmm_order = 12 if force_direct: fmm_order = False pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=fmm_order, _expansions_in_tree_have_extent=True, ) places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from meshmode.dof_array import thaw, flatten nodes = thaw(actx, density_discr.nodes()) if visualize: # plot geometry, centers, normals centers = bind(places, sym.expansion_centers(qbx.ambient_dim, +1))(actx) normals = bind(places, sym.normal(qbx.ambient_dim))(actx).as_vector(object) nodes_h = np.array( [actx.to_numpy(axis) for axis in flatten(nodes)]) centers_h = np.array( [actx.to_numpy(axis) for axis in flatten(centers)]) normals_h = np.array( [actx.to_numpy(axis) for axis in flatten(normals)]) pt.plot(nodes_h[0], nodes_h[1], "x-") pt.plot(centers_h[0], centers_h[1], "o") pt.quiver(nodes_h[0], nodes_h[1], normals_h[0], normals_h[1]) pt.gca().set_aspect("equal") pt.show() angle = actx.np.arctan2(nodes[1] * ellipse_aspect, nodes[0]) ellipse_fraction = ((1 - ellipse_aspect) / (1 + ellipse_aspect))**mode_nr # (2.6) in [1] J = actx.np.sqrt( # noqa actx.np.sin(angle)**2 + (1 / ellipse_aspect)**2 * actx.np.cos(angle)**2) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(2) # {{{ single layer sigma_sym = sym.var("sigma") s_sigma_op = sym.S(lap_knl, sigma_sym, qbx_forced_limit=+1) sigma = actx.np.cos(mode_nr * angle) / J s_sigma = bind(places, s_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) s_eigval = 1 / (2 * mode_nr) * (1 + (-1)**mode_nr * ellipse_fraction) # (2.12) in [1] s_sigma_ref = s_eigval * J * sigma if 0: #pt.plot(s_sigma.get(), label="result") #pt.plot(s_sigma_ref.get(), label="ref") pt.plot(actx.to_numpy(flatten(s_sigma_ref - s_sigma)), label="err") pt.legend() pt.show() h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_err = (norm(density_discr, s_sigma - s_sigma_ref) / norm(density_discr, s_sigma_ref)) s_eoc_rec.add_data_point(h_max, s_err) # }}} # {{{ double layer d_sigma_op = sym.D(lap_knl, sigma_sym, qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) d_sigma = bind(places, d_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) d_eigval = -(-1)**mode_nr * 1 / 2 * ellipse_fraction d_sigma_ref = d_eigval * sigma if 0: pt.plot(actx.to_numpy(flatten(d_sigma)), label="result") pt.plot(actx.to_numpy(flatten(d_sigma_ref)), label="ref") pt.legend() pt.show() if ellipse_aspect == 1: d_ref_norm = norm(density_discr, sigma) else: d_ref_norm = norm(density_discr, d_sigma_ref) d_err = (norm(density_discr, d_sigma - d_sigma_ref) / d_ref_norm) d_eoc_rec.add_data_point(h_max, d_err) # }}} if ellipse_aspect == 1: # {{{ S' sp_sigma_op = sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) sp_sigma = bind(places, sp_sigma_op)(actx, sigma=sigma) sp_eigval = 0 sp_sigma_ref = sp_eigval * sigma sp_err = (norm(density_discr, sp_sigma - sp_sigma_ref) / norm(density_discr, sigma)) sp_eoc_rec.add_data_point(h_max, sp_err) # }}} print("Errors for S:") print(s_eoc_rec) required_order = qbx_order + 1 assert s_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D:") print(d_eoc_rec) required_order = qbx_order assert d_eoc_rec.order_estimate() > required_order - 1.5 if ellipse_aspect == 1: print("Errors for S':") print(sp_eoc_rec) required_order = qbx_order assert sp_eoc_rec.order_estimate() > required_order - 1.5