def main(): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 16 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(nel_1d, ) * dim) order = 3 if dim == 2: # no deep meaning here, just a fudge factor dt = 0.75 / (nel_1d * order**2) elif dim == 3: # no deep meaning here, just a fudge factor dt = 0.45 / (nel_1d * order**2) else: raise ValueError("don't have a stable time step guesstimate") print("%d elements" % mesh.nelements) from meshmode.discretization.poly_element import \ QuadratureSimplexGroupFactory, \ PolynomialWarpAndBlendGroupFactory discr = EagerDGDiscretization( actx, mesh, quad_tag_to_group_factory={ QTAG_NONE: PolynomialWarpAndBlendGroupFactory(order), "vel_prod": QuadratureSimplexGroupFactory(3 * order), }) # bounded above by 1 c = 0.2 + 0.8 * bump(actx, discr, center=np.zeros(3), width=0.5) fields = flat_obj_array(bump( actx, discr, ), [discr.zeros(actx) for i in range(discr.dim)]) vis = make_visualizer(discr, order + 3 if dim == 2 else order) def rhs(t, w): return wave_operator(discr, c=c, w=w) t = 0 t_final = 3 istep = 0 while t < t_final: fields = rk4_step(fields, t, dt, rhs) if istep % 10 == 0: print(istep, t, discr.norm(fields[0])) vis.write_vtk_file("fld-wave-eager-var-velocity-%04d.vtu" % istep, [ ("c", c), ("u", fields[0]), ("v", fields[1:]), ]) t += dt istep += 1
def test_sanity_single_element(ctx_factory, dim, order, visualize=False): pytest.importorskip("pytential") cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) from modepy.tools import unit_vertices vertices = unit_vertices(dim).T.copy() center = np.empty(dim, np.float64) center.fill(-0.5) import modepy as mp from meshmode.mesh import SimplexElementGroup, Mesh, BTAG_ALL mg = SimplexElementGroup( order=order, vertex_indices=np.arange(dim+1, dtype=np.int32).reshape(1, -1), nodes=mp.warp_and_blend_nodes(dim, order).reshape(dim, 1, -1), dim=dim) mesh = Mesh(vertices, [mg], is_conforming=True) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory vol_discr = Discretization(cl_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order+3)) # {{{ volume calculation check vol_x = vol_discr.nodes().with_queue(queue) vol_one = vol_x[0].copy() vol_one.fill(1) from pytential import norm, integral # noqa from pytools import factorial true_vol = 1/factorial(dim) * 2**dim comp_vol = integral(vol_discr, queue, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol assert rel_vol_err < 1e-12 # }}} # {{{ boundary discretization from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction( vol_discr, PolynomialWarpAndBlendGroupFactory(order + 3), BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} # {{{ visualizers from meshmode.discretization.visualization import make_visualizer #vol_vis = make_visualizer(queue, vol_discr, 4) bdry_vis = make_visualizer(queue, bdry_discr, 4) # }}} from pytential import bind, sym bdry_normals = bind(bdry_discr, sym.normal(dim))(queue).as_vector(dtype=object) if visualize: bdry_vis.write_vtk_file("boundary.vtu", [ ("bdry_normals", bdry_normals) ]) normal_outward_check = bind(bdry_discr, sym.normal(dim) | (sym.nodes(dim) + 0.5*sym.ones_vec(dim)), )(queue).as_scalar() > 0 assert normal_outward_check.get().all(), normal_outward_check.get()
def test_diffusion_accuracy(actx_factory, problem, nsteps, dt, scales, order, visualize=False): """ Checks the accuracy of the diffusion operator by solving the heat equation for a given problem setup. """ actx = actx_factory() p = problem sym_x = pmbl.make_sym_vector("x", p.dim) sym_t = pmbl.var("t") sym_u = p.get_solution(sym_x, sym_t) sym_alpha = p.get_alpha(sym_x, sym_t, sym_u) sym_diffusion_u = sym_diffusion(p.dim, sym_alpha, sym_u) # In order to support manufactured solutions, we modify the heat equation # to add a source term f. If the solution is exact, this term should be 0. sym_f = sym.diff(sym_t)(sym_u) - sym_diffusion_u from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for n in scales: mesh = p.get_mesh(n) from grudge.eager import EagerDGDiscretization from meshmode.discretization.poly_element import \ QuadratureSimplexGroupFactory, \ PolynomialWarpAndBlendGroupFactory discr = EagerDGDiscretization( actx, mesh, discr_tag_to_group_factory={ DISCR_TAG_BASE: PolynomialWarpAndBlendGroupFactory(order), DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(3 * order), }) nodes = thaw(actx, discr.nodes()) def get_rhs(t, u): alpha = p.get_alpha(nodes, t, u) if isinstance(alpha, DOFArray): discr_tag = DISCR_TAG_QUAD else: discr_tag = DISCR_TAG_BASE return ( diffusion_operator(discr, quad_tag=discr_tag, alpha=alpha, boundaries=p.get_boundaries(discr, actx, t), u=u) + _sym_eval(sym_f, x=nodes, t=t)) t = 0. u = p.get_solution(nodes, t) from mirgecom.integrators import rk4_step for _ in range(nsteps): u = rk4_step(u, t, dt, get_rhs) t += dt expected_u = p.get_solution(nodes, t) rel_linf_err = actx.to_numpy( discr.norm(u - expected_u, np.inf) / discr.norm(expected_u, 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 + 3) vis.write_vtk_file( "diffusion_accuracy_{order}_{n}.vtu".format(order=order, n=n), [ ("u", u), ("expected_u", expected_u), ]) print("L^inf error:") print(eoc_rec) # Expected convergence rates from Hesthaven/Warburton book expected_order = order + 1 if order % 2 == 0 else order assert (eoc_rec.order_estimate() >= expected_order - 0.5 or eoc_rec.max_error() < 1e-11)
def build_connection_from_firedrake(actx, fdrake_fspace, grp_factory=None, restrict_to_boundary=None): """ Create a :class:`FiredrakeConnection` from a :mod:`firedrake` ``"DG"`` function space by creates a corresponding meshmode discretization and facilitating transfer of functions to and from :mod:`firedrake`. :arg actx: A :class:`~meshmode.array_context.ArrayContext` used to instantiate :attr:`FiredrakeConnection.discr`. :arg fdrake_fspace: A :mod:`firedrake` ``"DG"`` function space (of class :class:`~firedrake.functionspaceimpl.WithGeometry`) built on a mesh which is importable by :func:`~meshmode.interop.firedrake.mesh.import_firedrake_mesh`. :arg grp_factory: (optional) If not *None*, should be a :class:`~meshmode.discretization.poly_element.ElementGroupFactory` whose group class is a subclass of :class:`~meshmode.discretization.InterpolatoryElementGroupBase`. If *None*, and :mod:`recursivenodes` can be imported, a :class:`~meshmode.discretization.poly_element.\ PolynomialRecursiveNodesGroupFactory` with ``"lgl"`` nodes is used. Note that :mod:`recursivenodes` may not be importable as it uses :func:`math.comb`, which is new in Python 3.8. In the case that :mod:`recursivenodes` cannot be successfully imported, a :class:`~meshmode.discretization.poly_element.\ PolynomialWarpAndBlendGroupFactory` is used. :arg restrict_to_boundary: (optional) If not *None*, then must be a valid boundary marker for ``fdrake_fspace.mesh()``. In this case, creates a :class:`~meshmode.discretization.Discretization` on a submesh of ``fdrake_fspace.mesh()`` created from the cells with at least one vertex on a facet marked with the marker *restrict_to_boundary*. """ # Ensure fdrake_fspace is a function space with appropriate reference # element. from firedrake.functionspaceimpl import WithGeometry if not isinstance(fdrake_fspace, WithGeometry): raise TypeError("'fdrake_fspace' must be of firedrake type " "WithGeometry, not '%s'." % type(fdrake_fspace)) ufl_elt = fdrake_fspace.ufl_element() if ufl_elt.family() != "Discontinuous Lagrange": raise ValueError("the 'fdrake_fspace.ufl_element().family()' of " "must be be " "'Discontinuous Lagrange', not '%s'." % ufl_elt.family()) # Make sure grp_factory is the right type if provided, and # uses an interpolatory class. if grp_factory is not None: if not isinstance(grp_factory, ElementGroupFactory): raise TypeError("'grp_factory' must inherit from " "meshmode.discretization.ElementGroupFactory," "but is instead of type " "'%s'." % type(grp_factory)) if not issubclass(grp_factory.group_class, InterpolatoryElementGroupBase): raise TypeError("'grp_factory.group_class' must inherit from" "meshmode.discretization." "InterpolatoryElementGroupBase, but" " is instead of type '%s'" % type(grp_factory.group_class)) # If not provided, make one else: degree = ufl_elt.degree() try: # recursivenodes is only importable in Python 3.8 since # it uses :func:`math.comb`, so need to check if it can # be imported import recursivenodes # noqa : F401 family = "lgl" # L-G-Legendre grp_factory = PolynomialRecursiveNodesGroupFactory(degree, family) except ImportError: # If cannot be imported, uses warp-and-blend nodes grp_factory = PolynomialWarpAndBlendGroupFactory(degree) if restrict_to_boundary is not None: uniq_markers = fdrake_fspace.mesh().exterior_facets.unique_markers allowable_bdy_ids = list(uniq_markers) + ["on_boundary"] # make sure restrict_to_boundary is of correct type type_check = isinstance(restrict_to_boundary, int) if not type_check: is_tuple = isinstance(restrict_to_boundary, tuple) if is_tuple: type_check = all([isinstance(x, int) for x in restrict_to_boundary]) else: type_check = restrict_to_boundary == "on_boundary" if not type_check: raise TypeError("restrict_to_boundary must be an int, a tuple" " of ints, or the string 'on_boundary', not" f" of type {type(restrict_to_boundary)}") # convert int to tuple to avoid corner cases else: restrict_to_boundary = (restrict_to_boundary,) # make sure all markers are valid if restrict_to_boundary != "on_boundary": for marker in restrict_to_boundary: if marker not in allowable_bdy_ids: raise ValueError("'restrict_to_boundary' must be one of" " the following allowable boundary ids: " f"{allowable_bdy_ids}, not " f"'{restrict_to_boundary}'") # If only converting a portion of the mesh near the boundary, get # *cells_to_use* as described in # :func:`meshmode.interop.firedrake.mesh.import_firedrake_mesh` cells_to_use = _get_cells_to_use(fdrake_fspace.mesh(), restrict_to_boundary) # Create to_discr mm_mesh, orient = import_firedrake_mesh(fdrake_fspace.mesh(), cells_to_use=cells_to_use) to_discr = Discretization(actx, mm_mesh, grp_factory) # get firedrake unit nodes and map onto meshmode reference element group = to_discr.groups[0] fd_ref_cell_to_mm = get_affine_reference_simplex_mapping(group.dim, True) fd_unit_nodes = get_finat_element_unit_nodes(fdrake_fspace.finat_element) fd_unit_nodes = fd_ref_cell_to_mm(fd_unit_nodes) # Flipping negative elements corresponds to reordering the nodes. # We handle reordering by storing the permutation explicitly as # a numpy array # Get the reordering fd->mm. flip_mat = get_simplex_element_flip_matrix(ufl_elt.degree(), fd_unit_nodes) fd_cell_node_list = fdrake_fspace.cell_node_list if cells_to_use is not None: fd_cell_node_list = fd_cell_node_list[cells_to_use] # flip fd_cell_node_list flipped_cell_node_list = _reorder_nodes(orient, fd_cell_node_list, flip_mat, unflip=False) assert np.size(np.unique(flipped_cell_node_list)) == \ np.size(flipped_cell_node_list), \ "A firedrake node in a 'DG' space got duplicated" return FiredrakeConnection(to_discr, fdrake_fspace, flipped_cell_node_list)
def test_all_faces_interpolation(ctx_factory, mesh_name, dim, mesh_pars, per_face_groups): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) from meshmode.discretization import Discretization from meshmode.discretization.connection import ( make_face_restriction, make_face_to_all_faces_embedding, check_connection) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() order = 4 def f(x): return 0.1*cl.clmath.sin(30*x) for mesh_par in mesh_pars: # {{{ get mesh if mesh_name == "blob": assert dim == 2 h = mesh_par from meshmode.mesh.io import generate_gmsh, FileSource print("BEGIN GEN") mesh = generate_gmsh( FileSource("blob-2d.step"), 2, order=order, force_ambient_dim=2, other_options=[ "-string", "Mesh.CharacteristicLengthMax = %s;" % h] ) print("END GEN") elif mesh_name == "warp": from meshmode.mesh.generation import generate_warped_rect_mesh mesh = generate_warped_rect_mesh(dim, order=4, n=mesh_par) h = 1/mesh_par else: raise ValueError("mesh_name not recognized") # }}} vol_discr = Discretization(cl_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order)) print("h=%s -> %d elements" % ( h, sum(mgrp.nelements for mgrp in mesh.groups))) all_face_bdry_connection = make_face_restriction( vol_discr, PolynomialWarpAndBlendGroupFactory(order), FACE_RESTR_ALL, per_face_groups=per_face_groups) all_face_bdry_discr = all_face_bdry_connection.to_discr for ito_grp, ceg in enumerate(all_face_bdry_connection.groups): for ibatch, batch in enumerate(ceg.batches): assert np.array_equal( batch.from_element_indices.get(queue), np.arange(vol_discr.mesh.nelements)) if per_face_groups: assert ito_grp == batch.to_element_face else: assert ibatch == batch.to_element_face all_face_x = all_face_bdry_discr.nodes()[0].with_queue(queue) all_face_f = f(all_face_x) all_face_f_2 = all_face_bdry_discr.zeros(queue) for boundary_tag in [ BTAG_ALL, FACE_RESTR_INTERIOR, ]: bdry_connection = make_face_restriction( vol_discr, PolynomialWarpAndBlendGroupFactory(order), boundary_tag, per_face_groups=per_face_groups) bdry_discr = bdry_connection.to_discr bdry_x = bdry_discr.nodes()[0].with_queue(queue) bdry_f = f(bdry_x) all_face_embedding = make_face_to_all_faces_embedding( bdry_connection, all_face_bdry_discr) check_connection(all_face_embedding) all_face_f_2 += all_face_embedding(queue, bdry_f) err = la.norm((all_face_f-all_face_f_2).get(), np.inf) eoc_rec.add_data_point(h, err) print(eoc_rec) assert ( eoc_rec.order_estimate() >= order-0.5 or eoc_rec.max_error() < 1e-14)
def test_dof_array_arithmetic_same_as_numpy(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=(3,)*2, order=1) discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3)) def get_real(ary): return ary.real def get_imag(ary): return ary.real import operator from pytools import generate_nonnegative_integer_tuples_below as gnitb from random import uniform, randrange for op_func, n_args, use_integers in [ (operator.add, 2, False), (operator.sub, 2, False), (operator.mul, 2, False), (operator.truediv, 2, False), (operator.pow, 2, False), # FIXME pyopencl.Array doesn't do mod. #(operator.mod, 2, True), #(operator.mod, 2, False), #(operator.imod, 2, True), #(operator.imod, 2, False), # FIXME: Two outputs #(divmod, 2, False), (operator.iadd, 2, False), (operator.isub, 2, False), (operator.imul, 2, False), (operator.itruediv, 2, False), (operator.and_, 2, True), (operator.xor, 2, True), (operator.or_, 2, True), (operator.iand, 2, True), (operator.ixor, 2, True), (operator.ior, 2, True), (operator.ge, 2, False), (operator.lt, 2, False), (operator.gt, 2, False), (operator.eq, 2, True), (operator.ne, 2, True), (operator.pos, 1, False), (operator.neg, 1, False), (operator.abs, 1, False), (get_real, 1, False), (get_imag, 1, False), ]: for is_array_flags in gnitb(2, n_args): if sum(is_array_flags) == 0: # all scalars, no need to test continue if is_array_flags[0] == 0 and op_func in [ operator.iadd, operator.isub, operator.imul, operator.itruediv, operator.iand, operator.ixor, operator.ior, ]: # can't do in place operations with a scalar lhs continue args = [ (0.5+np.random.rand(discr.ndofs) if not use_integers else np.random.randint(3, 200, discr.ndofs)) if is_array_flag else (uniform(0.5, 2) if not use_integers else randrange(3, 200)) for is_array_flag in is_array_flags] # {{{ get reference numpy result # make a copy for the in place operators ref_args = [ arg.copy() if isinstance(arg, np.ndarray) else arg for arg in args] ref_result = op_func(*ref_args) # }}} # {{{ test DOFArrays actx_args = [ unflatten(actx, discr, actx.from_numpy(arg)) if isinstance(arg, np.ndarray) else arg for arg in args] actx_result = actx.to_numpy(flatten(op_func(*actx_args))) assert np.allclose(actx_result, ref_result) # }}} # {{{ test object arrays of DOFArrays # It would be very nice if comparisons on object arrays behaved # consistently with everything else. Alas, they do not. Instead: # # 0.5 < obj_array(DOFArray) -> obj_array([True]) # # because hey, 0.5 < DOFArray returned something truthy. if op_func not in [ operator.eq, operator.ne, operator.le, operator.lt, operator.ge, operator.gt, operator.iadd, operator.isub, operator.imul, operator.itruediv, operator.iand, operator.ixor, operator.ior, # All Python objects are real-valued, right? get_imag, ]: obj_array_args = [ make_obj_array([arg]) if isinstance(arg, DOFArray) else arg for arg in actx_args] obj_array_result = actx.to_numpy( flatten(op_func(*obj_array_args)[0])) assert np.allclose(obj_array_result, ref_result)
def main(ctx_factory, dim=2, order=4, product_tag=None, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ parameters # sphere radius radius = 1.0 # sphere resolution resolution = 64 if dim == 2 else 1 # cfl dt_factor = 2.0 # final time final_time = np.pi # velocity field sym_x = sym.nodes(dim) c = make_obj_array([-sym_x[1], sym_x[0], 0.0])[:dim] # flux flux_type = "lf" # }}} # {{{ discretization if dim == 2: from meshmode.mesh.generation import make_curve_mesh, ellipse mesh = make_curve_mesh(lambda t: radius * ellipse(1.0, t), np.linspace(0.0, 1.0, resolution + 1), order) elif dim == 3: from meshmode.mesh.generation import generate_icosphere mesh = generate_icosphere(radius, order=4 * order, uniform_refinement_rounds=resolution) else: raise ValueError("unsupported dimension") quad_tag_to_group_factory = {} if product_tag == "none": product_tag = None from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory, \ QuadratureSimplexGroupFactory quad_tag_to_group_factory[sym.QTAG_NONE] = \ PolynomialWarpAndBlendGroupFactory(order) if product_tag: quad_tag_to_group_factory[product_tag] = \ QuadratureSimplexGroupFactory(order=4*order) from grudge import DGDiscretizationWithBoundaries discr = DGDiscretizationWithBoundaries( actx, mesh, quad_tag_to_group_factory=quad_tag_to_group_factory) volume_discr = discr.discr_from_dd(sym.DD_VOLUME) logger.info("ndofs: %d", volume_discr.ndofs) logger.info("nelements: %d", volume_discr.mesh.nelements) # }}} # {{{ symbolic operators def f_initial_condition(x): return x[0] from grudge.models.advection import SurfaceAdvectionOperator op = SurfaceAdvectionOperator(c, flux_type=flux_type, quad_tag=product_tag) bound_op = bind(discr, op.sym_operator()) u0 = bind(discr, f_initial_condition(sym_x))(actx, t=0) def rhs(t, u): return bound_op(actx, t=t, u=u) # check velocity is tangential sym_normal = sym.surface_normal(dim, dim=dim - 1, dd=sym.DD_VOLUME).as_vector() error = bind(discr, sym.norm(2, c.dot(sym_normal)))(actx) logger.info("u_dot_n: %.5e", error) # }}} # {{{ time stepping # compute time step h_min = bind(discr, sym.h_max_from_volume(discr.ambient_dim, dim=discr.dim))(actx) dt = dt_factor * h_min / order**2 nsteps = int(final_time // dt) + 1 dt = final_time / nsteps + 1.0e-15 logger.info("dt: %.5e", dt) logger.info("nsteps: %d", nsteps) from grudge.shortcuts import set_up_rk4 dt_stepper = set_up_rk4("u", dt, u0, rhs) plot = Plotter(actx, discr, order, visualize=visualize) norm = bind(discr, sym.norm(2, sym.var("u"))) norm_u = norm(actx, u=u0) step = 0 event = dt_stepper.StateComputed(0.0, 0, 0, u0) plot(event, "fld-surface-%04d" % 0) if visualize and dim == 3: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, vis_order=order) vis.write_vtk_file("fld-surface-velocity.vtu", [("u", bind(discr, c)(actx)), ("n", bind(discr, sym_normal)(actx))], overwrite=True) df = sym.DOFDesc(sym.FACE_RESTR_INTERIOR) face_discr = discr.connection_from_dds(sym.DD_VOLUME, df).to_discr face_normal = bind( discr, sym.normal(df, face_discr.ambient_dim, dim=face_discr.dim))(actx) from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, face_discr, vis_order=order) vis.write_vtk_file("fld-surface-face-normals.vtu", [("n", face_normal)], overwrite=True) for event in dt_stepper.run(t_end=final_time, max_steps=nsteps + 1): if not isinstance(event, dt_stepper.StateComputed): continue step += 1 if step % 10 == 0: norm_u = norm(actx, u=event.state_component) plot(event, "fld-surface-%04d" % step) logger.info("[%04d] t = %.5f |u| = %.5e", step, event.t, norm_u) plot(event, "fld-surface-%04d" % step)
def _test_mpi_boundary_swap(dim, order, num_groups): from meshmode.distributed import MPIMeshDistributor, MPIBoundaryCommSetupHelper from mpi4py import MPI mpi_comm = MPI.COMM_WORLD i_local_part = mpi_comm.Get_rank() num_parts = mpi_comm.Get_size() mesh_dist = MPIMeshDistributor(mpi_comm) if mesh_dist.is_mananger_rank(): np.random.seed(42) from meshmode.mesh.generation import generate_warped_rect_mesh meshes = [generate_warped_rect_mesh(dim, order=order, n=4) for _ in range(num_groups)] if num_groups > 1: from meshmode.mesh.processing import merge_disjoint_meshes mesh = merge_disjoint_meshes(meshes) else: mesh = meshes[0] part_per_element = np.random.randint(num_parts, size=mesh.nelements) local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts) else: local_mesh = mesh_dist.receive_mesh_part() group_factory = PolynomialWarpAndBlendGroupFactory(order) cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from meshmode.discretization import Discretization vol_discr = Discretization(actx, local_mesh, group_factory) from meshmode.distributed import get_connected_partitions connected_parts = get_connected_partitions(local_mesh) assert i_local_part not in connected_parts bdry_setup_helpers = {} local_bdry_conns = {} from meshmode.discretization.connection import make_face_restriction from meshmode.mesh import BTAG_PARTITION for i_remote_part in connected_parts: local_bdry_conns[i_remote_part] = make_face_restriction( actx, vol_discr, group_factory, BTAG_PARTITION(i_remote_part)) setup_helper = bdry_setup_helpers[i_remote_part] = \ MPIBoundaryCommSetupHelper( mpi_comm, actx, local_bdry_conns[i_remote_part], i_remote_part, bdry_grp_factory=group_factory) setup_helper.post_sends() remote_to_local_bdry_conns = {} from meshmode.discretization.connection import check_connection while bdry_setup_helpers: for i_remote_part, setup_helper in bdry_setup_helpers.items(): if setup_helper.is_setup_ready(): assert bdry_setup_helpers.pop(i_remote_part) is setup_helper conn = setup_helper.complete_setup() check_connection(actx, conn) remote_to_local_bdry_conns[i_remote_part] = conn break # FIXME: Not ideal, busy-waits _test_data_transfer(mpi_comm, actx, local_bdry_conns, remote_to_local_bdry_conns, connected_parts) logger.debug("Rank %d exiting", i_local_part)
def build_kernel_exterior_normalizer_table(self, cl_ctx, queue, pool=None, ncpus=None, mesh_order=5, quad_order=10, mesh_size=0.03, remove_tmp_files=True, **kwargs): r"""Build the kernel exterior normalizer table for fractional Laplacians. An exterior normalizer for kernel :math:`G(r)` and target :math:`x` is defined as .. math:: \int_{B^c} G(\lVert x - y \rVert) dy where :math:`B` is the source box :math:`[0, source_box_extent]^dim`. """ logger.warn("this method is currently under construction.") if not self.inverse_droste: raise ValueError() if ncpus is None: import multiprocessing ncpus = multiprocessing.cpu_count() if pool is None: from multiprocessing import Pool pool = Pool(ncpus) def fl_scaling(k, s): # scaling constant from scipy.special import gamma return (2**(2 * s) * s * gamma(s + k / 2)) / (np.pi**(k / 2) * gamma(1 - s)) # Directly compute and return in 1D if self.dim == 1: s = self.integral_knl.s targets = np.array(self.q_points).reshape(-1) r1 = targets r2 = self.source_box_extent - targets self.kernel_exterior_normalizers = 1 / (2 * s) * ( 1 / r1**(2 * s) + 1 / r2**(2 * s)) * fl_scaling(k=self.dim, s=s) return from meshmode.array_context import PyOpenCLArrayContext from meshmode.dof_array import thaw, flatten from meshmode.mesh.io import read_gmsh from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory # {{{ gmsh processing import gmsh gmsh.initialize() gmsh.option.setNumber("General.Terminal", 1) # meshmode does not support other versions gmsh.option.setNumber("Mesh.MshFileVersion", 2) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", mesh_size) gmsh.option.setNumber("Mesh.ElementOrder", mesh_order) if mesh_order > 1: gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 1) # radius of source box hs = self.source_box_extent / 2 # radius of bouding sphere r = hs * np.sqrt(self.dim) logger.debug("r_inner = %f, r_outer = %f" % (hs, r)) if self.dim == 2: tag_box = gmsh.model.occ.addRectangle(x=0, y=0, z=0, dx=2 * hs, dy=2 * hs, tag=-1) elif self.dim == 3: tag_box = gmsh.model.occ.addBox(x=0, y=0, z=0, dx=2 * hs, dy=2 * hs, dz=2 * hs, tag=-1) else: raise NotImplementedError() if self.dim == 2: tag_ball = gmsh.model.occ.addDisk(xc=hs, yc=hs, zc=0, rx=r, ry=r, tag=-1) elif self.dim == 3: tag_sphere = gmsh.model.occ.addSphere(xc=hs, yc=hs, zc=hs, radius=r, tag=-1) tag_ball = gmsh.model.occ.addVolume([tag_sphere], tag=-1) else: raise NotImplementedError() dimtags_ints, dimtags_map_ints = gmsh.model.occ.cut( objectDimTags=[(self.dim, tag_ball)], toolDimTags=[(self.dim, tag_box)], tag=-1, removeObject=True, removeTool=True) gmsh.model.occ.synchronize() gmsh.model.mesh.generate(self.dim) from tempfile import mkdtemp from os.path import join temp_dir = mkdtemp(prefix="tmp_volumential_nft") msh_filename = join(temp_dir, 'chinese_lucky_coin.msh') gmsh.write(msh_filename) gmsh.finalize() mesh = read_gmsh(msh_filename) if remove_tmp_files: import shutil shutil.rmtree(temp_dir) # }}} End gmsh processing arr_ctx = PyOpenCLArrayContext(queue) discr = Discretization( arr_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order=quad_order)) from pytential import bind, sym # {{{ optional checks if 1: if self.dim == 2: arerr = np.abs((np.pi * r**2 - (2 * hs)**2) - bind(discr, sym.integral(self.dim, self.dim, 1)) (queue)) / (np.pi * r**2 - (2 * hs)**2) if arerr > 1e-12: log_to = logger.warn else: log_to = logger.debug log_to("the numerical error when computing the measure of a " "unit ball is %e" % arerr) elif self.dim == 3: arerr = np.abs((4 / 3 * np.pi * r**3 - (2 * hs)**3) - bind(discr, sym.integral(self.dim, self.dim, 1)) (queue)) / (4 / 3 * np.pi * r**3 - (2 * hs)**3) if arerr > 1e-12: log_to = logger.warn else: log_to = logger.debug logger.warn( "The numerical error when computing the measure of a " "unit ball is %e" % arerr) # }}} End optional checks # {{{ kernel evaluation # TODO: take advantage of symmetry if this is too slow from volumential.droste import InverseDrosteReduced # only for getting kernel evaluation related stuff drf = InverseDrosteReduced(self.integral_knl, self.quad_order, self.interaction_case_vecs, n_brick_quad_points=0, knl_symmetry_tags=[], auto_windowing=False) # uses "dist[dim]", assigned to "knl_val" knl_insns = drf.get_sumpy_kernel_insns() eval_kernel_insns = [ insn.copy(within_inames=insn.within_inames | frozenset(["iqpt"])) for insn in knl_insns ] from sumpy.symbolic import SympyToPymbolicMapper sympy_conv = SympyToPymbolicMapper() scaling_assignment = lp.Assignment( id=None, assignee="knl_scaling", expression=sympy_conv( self.integral_knl.get_global_scaling_const()), temp_var_type=lp.Optional(), ) extra_kernel_kwarg_types = () if "extra_kernel_kwarg_types" in kwargs: extra_kernel_kwarg_types = kwargs["extra_kernel_kwarg_types"] lpknl = lp.make_kernel( # NOQA "{ [iqpt, iaxis]: 0<=iqpt<n_q_points and 0<=iaxis<dim }", [ """ for iqpt for iaxis <> dist[iaxis] = (quad_points[iaxis, iqpt] - target_point[iaxis]) end end """ ] + eval_kernel_insns + [scaling_assignment] + [ """ for iqpt result[iqpt] = knl_val * knl_scaling end """ ], [ lp.ValueArg("dim, n_q_points", np.int32), lp.GlobalArg("quad_points", np.float64, "dim, n_q_points"), lp.GlobalArg("target_point", np.float64, "dim") ] + list(extra_kernel_kwarg_types) + [ "...", ], name="eval_kernel_lucky_coin", lang_version=(2018, 2), ) lpknl = lp.fix_parameters(lpknl, dim=self.dim) lpknl = lp.set_options(lpknl, write_cl=False) lpknl = lp.set_options(lpknl, return_dict=True) # }}} End kernel evaluation node_coords = flatten(thaw(arr_ctx, discr.nodes())) nodes = cl.array.to_device( queue, np.vstack([crd.get() for crd in node_coords])) int_vals = [] for target in self.q_points: evt, res = lpknl(queue, quad_points=nodes, target_point=target) knl_vals = res['result'] integ = bind( discr, sym.integral(self.dim, self.dim, sym.var("integrand")))(queue, integrand=knl_vals) queue.finish() int_vals.append(integ) int_vals_coins = np.array(int_vals) int_vals_inf = np.zeros(self.n_q_points) # {{{ integrate over the exterior of the ball if self.dim == 2: def rho_0(theta, target, radius): rho_x = np.linalg.norm(target, ord=2) return (-1 * rho_x * np.cos(theta) + np.sqrt(radius**2 - rho_x**2 * (np.sin(theta)**2))) def ext_inf_integrand(theta, s, target, radius): _rho_0 = rho_0(theta, target=target, radius=radius) return _rho_0**(-2 * s) def compute_ext_inf_integral(target, s, radius): # target: target point # s: fractional order # radius: radius of the circle import scipy.integrate as sint val, _ = sint.quadrature(partial(ext_inf_integrand, s=s, target=target, radius=radius), a=0, b=2 * np.pi) return val * (1 / (2 * s)) * fl_scaling(k=self.dim, s=s) if 1: # optional test target = [0, 0] s = 0.5 radius = 1 scaling = fl_scaling(k=self.dim, s=s) val = compute_ext_inf_integral(target, s, radius) test_err = np.abs(val - radius**(-2 * s) * 2 * np.pi * (1 / (2 * s)) * scaling) / (radius**(-2 * s) * 2 * np.pi * (1 / (2 * s)) * scaling) if test_err > 1e-12: logger.warn("Error evaluating at origin = %f" % test_err) for tid, target in enumerate(self.q_points): # The formula assumes that the source box is centered at origin int_vals_inf[tid] = compute_ext_inf_integral( target=target - hs, s=self.integral_knl.s, radius=r) elif self.dim == 3: # FIXME raise NotImplementedError("3D not yet implemented.") else: raise NotImplementedError("Unsupported dimension") # }}} End integrate over the exterior of the ball self.kernel_exterior_normalizers = int_vals_coins + int_vals_inf return
def __init__( self, array_context, mesh, order=None, discr_tag_to_group_factory=None, mpi_communicator=None, # FIXME: `quad_tag_to_group_factory` is deprecated quad_tag_to_group_factory=None): """ :param discr_tag_to_group_factory: A mapping from discretization tags (typically one of: :class:`grudge.dof_desc.DISCR_TAG_BASE`, :class:`grudge.dof_desc.DISCR_TAG_MODAL`, or :class:`grudge.dof_desc.DISCR_TAG_QUAD`) to a :class:`~meshmode.discretization.poly_element.ElementGroupFactory` indicating with which type of discretization the operations are to be carried out, or *None* to indicate that operations with this discretization tag should be carried out with the standard volume discretization. """ if (quad_tag_to_group_factory is not None and discr_tag_to_group_factory is not None): raise ValueError( "Both `quad_tag_to_group_factory` and `discr_tag_to_group_factory` " "are specified. Use `discr_tag_to_group_factory` instead.") # FIXME: `quad_tag_to_group_factory` is deprecated if (quad_tag_to_group_factory is not None and discr_tag_to_group_factory is None): warn( "`quad_tag_to_group_factory` is a deprecated kwarg and will " "be dropped in version 2022.x. Use `discr_tag_to_group_factory` " "instead.", DeprecationWarning, stacklevel=2) discr_tag_to_group_factory = quad_tag_to_group_factory self._setup_actx = array_context from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory if discr_tag_to_group_factory is None: if order is None: raise TypeError( "one of 'order' and 'discr_tag_to_group_factory' must be given" ) discr_tag_to_group_factory = { DISCR_TAG_BASE: PolynomialWarpAndBlendGroupFactory(order=order) } else: if order is not None: discr_tag_to_group_factory = discr_tag_to_group_factory.copy() if DISCR_TAG_BASE in discr_tag_to_group_factory: raise ValueError( "if 'order' is given, 'discr_tag_to_group_factory' must " "not have a key of DISCR_TAG_BASE") discr_tag_to_group_factory[DISCR_TAG_BASE] = \ PolynomialWarpAndBlendGroupFactory(order=order) # Modal discr should always comes from the base discretization discr_tag_to_group_factory[DISCR_TAG_MODAL] = \ _generate_modal_group_factory( discr_tag_to_group_factory[DISCR_TAG_BASE] ) self.discr_tag_to_group_factory = discr_tag_to_group_factory from meshmode.discretization import Discretization self._volume_discr = Discretization( array_context, mesh, self.group_factory_for_discretization_tag(DISCR_TAG_BASE)) # {{{ management of discretization-scoped common subexpressions from pytools import UniqueNameGenerator self._discr_scoped_name_gen = UniqueNameGenerator() self._discr_scoped_subexpr_to_name = {} self._discr_scoped_subexpr_name_to_value = {} # }}} self._dist_boundary_connections = \ self._set_up_distributed_communication( mpi_communicator, array_context) self.mpi_communicator = mpi_communicator
def drive_test_from_meshmode_interpolation(cl_ctx, queue, dim, degree, nel_1d, n_levels, q_order, a=-0.5, b=0.5, seed=0, test_case='exact'): """ meshmode mesh control: nel_1d, degree volumential mesh control: n_levels, q_order """ mesh = generate_regular_rect_mesh(a=(a, ) * dim, b=(b, ) * dim, n=(nel_1d, ) * dim) arr_ctx = PyOpenCLArrayContext(queue) group_factory = PolynomialWarpAndBlendGroupFactory(order=degree) discr = Discretization(arr_ctx, mesh, group_factory) bbox_fac = BoundingBoxFactory(dim=dim) boxfmm_fac = BoxFMMGeometryFactory(cl_ctx, dim=dim, order=q_order, nlevels=n_levels, bbox_getter=bbox_fac, expand_to_hold_mesh=mesh, mesh_padding_factor=0.) boxgeo = boxfmm_fac(queue) lookup_fac = ElementsToSourcesLookupBuilder(cl_ctx, tree=boxgeo.tree, discr=discr) lookup, evt = lookup_fac(queue) if test_case == 'exact': # algebraically exact interpolation func = random_polynomial_func(dim, degree, seed) elif test_case == 'non-exact': if dim == 2: def func(pts): x, y = pts return np.sin(x + y) * x elif dim == 3: def func(pts): x, y, z = pts return np.sin(x + y * z) * x else: raise ValueError() else: raise ValueError() dof_vec = eval_func_on_discr_nodes(queue, discr, func) res = interpolate_from_meshmode(queue, dof_vec, lookup).get(queue) tree = boxgeo.tree.get(queue) ref = func(np.vstack(tree.sources)) if test_case == 'exact': return np.allclose(ref, res) resid = np.linalg.norm(ref - res, ord=np.inf) return resid
def refine_and_generate_chart_function(mesh, filename, function): from time import clock cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) print("NELEMENTS: ", mesh.nelements) #print mesh for i in range(len(mesh.groups[0].vertex_indices[0])): for k in range(len(mesh.vertices)): print(mesh.vertices[k, i]) #check_nodal_adj_against_geometry(mesh); r = Refiner(mesh) #random.seed(0) #times = 3 num_elements = [] time_t = [] #nelements = mesh.nelements while True: print("NELS:", mesh.nelements) #flags = get_corner_flags(mesh) flags = get_function_flags(mesh, function) nels = 0 for i in flags: if i: nels += 1 if nels == 0: break print("LKJASLFKJALKASF:", nels) num_elements.append(nels) #flags = get_corner_flags(mesh) beg = clock() mesh = r.refine(flags) end = clock() time_taken = end - beg time_t.append(time_taken) #if nelements == mesh.nelements: #break #nelements = mesh.nelements #from meshmode.mesh.visualization import draw_2d_mesh #draw_2d_mesh(mesh, True, True, True, fill=None) #import matplotlib.pyplot as pt #pt.show() #poss_flags = np.zeros(len(mesh.groups[0].vertex_indices)) #for i in range(0, len(flags)): # poss_flags[i] = flags[i] #for i in range(len(flags), len(poss_flags)): # poss_flags[i] = 1 import matplotlib.pyplot as pt pt.xlabel('Number of elements being refined') pt.ylabel('Time taken') pt.plot(num_elements, time_t, "o") pt.savefig(filename, format='pdf') pt.clf() print('DONE REFINING') ''' flags = np.zeros(len(mesh.groups[0].vertex_indices)) flags[0] = 1 flags[1] = 1 mesh = r.refine(flags) flags = np.zeros(len(mesh.groups[0].vertex_indices)) flags[0] = 1 flags[1] = 1 flags[2] = 1 mesh = r.refine(flags) ''' #check_nodal_adj_against_geometry(mesh) #r.print_rays(70) #r.print_rays(117) #r.print_hanging_elements(10) #r.print_hanging_elements(117) #r.print_hanging_elements(757) #from meshmode.mesh.visualization import draw_2d_mesh #draw_2d_mesh(mesh, False, False, False, fill=None) #import matplotlib.pyplot as pt #pt.show() from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory discr = Discretization(cl_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order)) from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(queue, discr, order) remove_if_exists("connectivity2.vtu") remove_if_exists("geometry2.vtu") vis.write_vtk_file("geometry2.vtu", [ ("f", discr.nodes()[0]), ]) from meshmode.discretization.visualization import \ write_nodal_adjacency_vtk_file write_nodal_adjacency_vtk_file("connectivity2.vtu", mesh)