def get_mesh(): """Import a grid using `gmsh`. Input required: data/mesh.msh (read existing mesh) This routine will generate a new grid if it does not find the grid file (data/mesh.msh). """ from meshmode.mesh.io import (read_gmsh, generate_gmsh, ScriptWithFilesSource) import os if os.path.exists("data/mesh.msh") is False: char_len = 0.001 box_ll = (0.0, 0.0) box_ur = (0.25, 0.01) num_elements = (int((box_ur[0] - box_ll[0]) / char_len), int((box_ur[1] - box_ll[1]) / char_len)) from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=box_ll, b=box_ur, n=num_elements, boundary_tag_to_face={ "Inflow": ["-x"], "Outflow": ["+x"], "Wall": ["+y", "-y"] }) print("%d elements" % mesh.nelements) else: mesh = read_gmsh("data/mesh.msh") return mesh
def get_blob_mesh(mesh_par, order=4): # from meshmode.mesh.io import generate_gmsh, FileSource # return generate_gmsh( # FileSource("blob-2d.step"), 2, order=order, # force_ambient_dim=2, # other_options=[ # "-string", "Mesh.CharacteristicLengthMax = %s;" % mesh_par] # ) from meshmode.mesh.io import read_gmsh return read_gmsh("blob2d-order%d-h%s.msh" % (order, mesh_par), force_ambient_dim=2)
def get_blob_mesh(mesh_par, order=4): # from meshmode.mesh.io import generate_gmsh, FileSource # return generate_gmsh( # FileSource("blob-2d.step"), 2, order=order, # force_ambient_dim=2, # other_options=[ # "-string", "Mesh.CharacteristicLengthMax = %s;" % mesh_par] # ) from meshmode.mesh.io import read_gmsh return read_gmsh( "blob2d-order%d-h%s.msh" % (order, mesh_par), force_ambient_dim=2)
def test_boundary_tags(): from meshmode.mesh.io import read_gmsh # ensure tags are read in mesh = read_gmsh('annulus.msh') if not {'outer_bdy', 'inner_bdy'} <= set(mesh.boundary_tags): print("Mesh boundary tags:", mesh.boundary_tags) raise ValueError('Tags not saved by mesh') # correct answers num_on_outer_bdy = 26 num_on_inner_bdy = 13 # check how many elements are marked on each boundary num_marked_outer_bdy = 0 num_marked_inner_bdy = 0 outer_btag_bit = mesh.boundary_tag_bit('outer_bdy') inner_btag_bit = mesh.boundary_tag_bit('inner_bdy') for igrp in range(len(mesh.groups)): bdry_fagrp = mesh.facial_adjacency_groups[igrp].get(None, None) if bdry_fagrp is None: continue for i, nbrs in enumerate(bdry_fagrp.neighbors): if (-nbrs) & outer_btag_bit: num_marked_outer_bdy += 1 if (-nbrs) & inner_btag_bit: num_marked_inner_bdy += 1 # raise errors if wrong number of elements marked if num_marked_inner_bdy != num_on_inner_bdy: raise ValueError("%i marked on inner boundary, should be %i" % (num_marked_inner_bdy, num_on_inner_bdy)) if num_marked_outer_bdy != num_on_outer_bdy: raise ValueError("%i marked on outer boundary, should be %i" % (num_marked_outer_bdy, num_on_outer_bdy)) # ensure boundary is covered from meshmode.mesh import check_bc_coverage check_bc_coverage(mesh, ['inner_bdy', 'outer_bdy'])
def test_boundary_tags(): from meshmode.mesh.io import read_gmsh # ensure tags are read in mesh = read_gmsh('annulus.msh') if not {'outer_bdy', 'inner_bdy'} <= set(mesh.boundary_tags): print("Mesh boundary tags:", mesh.boundary_tags) raise ValueError('Tags not saved by mesh') # correct answers num_on_outer_bdy = 26 num_on_inner_bdy = 13 # check how many elements are marked on each boundary num_marked_outer_bdy = 0 num_marked_inner_bdy = 0 outer_btag_bit = mesh.boundary_tag_bit('outer_bdy') inner_btag_bit = mesh.boundary_tag_bit('inner_bdy') for igrp in range(len(mesh.groups)): bdry_fagrp = mesh.facial_adjacency_groups[igrp].get(None, None) if bdry_fagrp is None: continue for i, nbrs in enumerate(bdry_fagrp.neighbors): if (-nbrs) & outer_btag_bit: num_marked_outer_bdy += 1 if (-nbrs) & inner_btag_bit: num_marked_inner_bdy += 1 # raise errors if wrong number of elements marked if num_marked_inner_bdy != num_on_inner_bdy: raise ValueError("%i marked on inner boundary, should be %i" % (num_marked_inner_bdy, num_on_inner_bdy)) if num_marked_outer_bdy != num_on_outer_bdy: raise ValueError("%i marked on outer boundary, should be %i" % (num_marked_outer_bdy, num_on_outer_bdy)) # ensure boundary is covered from meshmode.mesh import check_bc_coverage check_bc_coverage(mesh, ['inner_bdy', 'outer_bdy'])
def get_fdrake_mesh_and_h_from_par(mesh_par): if fdrake_mesh_name == "UnitInterval": assert dim == 1 n = mesh_par fdrake_mesh = UnitIntervalMesh(n) h = 1 / n elif fdrake_mesh_name == "UnitSquare": assert dim == 2 n = mesh_par fdrake_mesh = UnitSquareMesh(n, n) h = 1 / n elif fdrake_mesh_name == "UnitCube": assert dim == 3 n = mesh_par fdrake_mesh = UnitCubeMesh(n, n, n) h = 1 / n elif fdrake_mesh_name in ("blob2d-order1", "blob2d-order4"): assert dim == 2 if fdrake_mesh_name == "blob2d-order1": from firedrake import Mesh fdrake_mesh = Mesh(f"{fdrake_mesh_name}-h{mesh_par}.msh", dim=dim) else: from meshmode.mesh.io import read_gmsh from meshmode.interop.firedrake import export_mesh_to_firedrake mm_mesh = read_gmsh(f"{fdrake_mesh_name}-h{mesh_par}.msh", force_ambient_dim=dim) fdrake_mesh, _, _ = export_mesh_to_firedrake(mm_mesh) h = float(mesh_par) elif fdrake_mesh_name == "warp": from meshmode.mesh.generation import generate_warped_rect_mesh from meshmode.interop.firedrake import export_mesh_to_firedrake mm_mesh = generate_warped_rect_mesh(dim, order=4, nelements_side=mesh_par) fdrake_mesh, _, _ = export_mesh_to_firedrake(mm_mesh) h = 1 / mesh_par else: raise ValueError("fdrake_mesh_name not recognized") return (fdrake_mesh, h)
def test_boundary_interpolation(actx_factory, group_factory, boundary_tag, mesh_name, dim, mesh_pars, per_face_groups): if (group_factory is LegendreGaussLobattoTensorProductGroupFactory and mesh_name == "blob"): pytest.skip("tensor products not implemented on blobs") actx = actx_factory() if group_factory is LegendreGaussLobattoTensorProductGroupFactory: group_cls = TensorProductElementGroup else: group_cls = SimplexElementGroup from meshmode.discretization import Discretization from meshmode.discretization.connection import (make_face_restriction, check_connection) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() order = 4 def f(x): return 0.1 * actx.np.sin(30 * x) for mesh_par in mesh_pars: # {{{ get mesh if mesh_name == "blob": assert dim == 2 h = float(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") from meshmode.mesh.io import read_gmsh mesh = read_gmsh("blob2d-order%d-h%s.msh" % (order, mesh_par), force_ambient_dim=2) elif mesh_name == "warp": mesh = mgen.generate_warped_rect_mesh(dim, order=order, nelements_side=mesh_par, group_cls=group_cls) h = 1 / mesh_par elif mesh_name == "rect": mesh = mgen.generate_regular_rect_mesh( a=(0, ) * dim, b=(1, ) * dim, order=order, nelements_per_axis=(mesh_par, ) * dim, group_cls=group_cls) h = 1 / mesh_par else: raise ValueError("mesh_name not recognized") # }}} vol_discr = Discretization(actx, mesh, group_factory(order)) print("h=%s -> %d elements" % (h, sum(mgrp.nelements for mgrp in mesh.groups))) x = thaw(vol_discr.nodes()[0], actx) vol_f = f(x) bdry_connection = make_face_restriction( actx, vol_discr, group_factory(order), boundary_tag, per_face_groups=per_face_groups) check_connection(actx, bdry_connection) bdry_discr = bdry_connection.to_discr bdry_x = thaw(bdry_discr.nodes()[0], actx) bdry_f = f(bdry_x) bdry_f_2 = bdry_connection(vol_f) if mesh_name == "blob" and dim == 2 and mesh.nelements < 500: from meshmode.discretization.connection.direct import \ make_direct_full_resample_matrix mat = actx.to_numpy( make_direct_full_resample_matrix(actx, bdry_connection)) bdry_f_2_by_mat = mat.dot(flatten_to_numpy(actx, vol_f)) mat_error = la.norm( flatten_to_numpy(actx, bdry_f_2) - bdry_f_2_by_mat) assert mat_error < 1e-14, mat_error err = flat_norm(bdry_f - bdry_f_2, np.inf) eoc_rec.add_data_point(h, err) order_slack = 0.75 if mesh_name == "blob" else 0.5 print(eoc_rec) assert (eoc_rec.order_estimate() >= order - order_slack or eoc_rec.max_error() < 3.6e-13)
def mm_mesh(request): from meshmode.mesh.io import read_gmsh return read_gmsh(request.param)
def test_to_fd_transfer(ctx_factory, fspace_degree, mesh_name, mesh_pars, dim): """ Make sure creating a function which projects onto one dimension then transports it is the same (up to resampling error) as projecting to one dimension on the transported mesh """ # build estimate-of-convergence recorder from pytools.convergence import EOCRecorder # dimension projecting onto -> EOCRecorder eoc_recorders = {d: EOCRecorder() for d in range(dim)} # make a computing context cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # Get each of the refinements of the meshmeshes and record # conversions errors for mesh_par in mesh_pars: if mesh_name in ("blob2d-order1", "blob2d-order4"): assert dim == 2 from meshmode.mesh.io import read_gmsh mm_mesh = read_gmsh(f"{mesh_name}-h{mesh_par}.msh", force_ambient_dim=dim) h = float(mesh_par) elif mesh_name == "warp": from meshmode.mesh.generation import generate_warped_rect_mesh mm_mesh = generate_warped_rect_mesh(dim, order=4, n=mesh_par) h = 1 / mesh_par else: raise ValueError("mesh_name not recognized") # Make discr and connect it to firedrake factory = InterpolatoryQuadratureSimplexGroupFactory(fspace_degree) discr = Discretization(actx, mm_mesh, factory) fdrake_connection = build_connection_to_firedrake(discr) fdrake_fspace = fdrake_connection.firedrake_fspace() spatial_coord = SpatialCoordinate(fdrake_fspace.mesh()) # get the group's nodes in a numpy array nodes = discr.nodes() group_nodes = np.array( [actx.to_numpy(dof_arr[0]) for dof_arr in nodes]) for d in range(dim): meshmode_f = discr.zeros(actx) meshmode_f[0][:] = group_nodes[d, :, :] # connect to firedrake and evaluate expr in firedrake fdrake_f = Function(fdrake_fspace).interpolate(spatial_coord[d]) # transport to firedrake and record error mm2fd_f = fdrake_connection.from_meshmode(meshmode_f) err = np.max(np.abs(fdrake_f.dat.data - mm2fd_f.dat.data)) eoc_recorders[d].add_data_point(h, err) # assert that order is correct or error is "low enough" for d, eoc_rec in eoc_recorders.items(): print("\nvector *x* -> *x[%s]*\n" % d, eoc_rec) assert (eoc_rec.order_estimate() >= fspace_degree or eoc_rec.max_error() < 2e-14)
def test_boundary_interpolation(ctx_factory, group_factory, boundary_tag, 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, 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 = float(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") from meshmode.mesh.io import read_gmsh mesh = read_gmsh("blob2d-order%d-h%s.msh" % (order, mesh_par), force_ambient_dim=2) 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, group_factory(order)) print("h=%s -> %d elements" % (h, sum(mgrp.nelements for mgrp in mesh.groups))) x = vol_discr.nodes()[0].with_queue(queue) vol_f = f(x) bdry_connection = make_face_restriction( vol_discr, group_factory(order), boundary_tag, per_face_groups=per_face_groups) check_connection(bdry_connection) bdry_discr = bdry_connection.to_discr bdry_x = bdry_discr.nodes()[0].with_queue(queue) bdry_f = f(bdry_x) bdry_f_2 = bdry_connection(queue, vol_f) if mesh_name == "blob" and dim == 2 and mesh.nelements < 500: mat = bdry_connection.full_resample_matrix(queue).get(queue) bdry_f_2_by_mat = mat.dot(vol_f.get()) mat_error = la.norm(bdry_f_2.get(queue=queue) - bdry_f_2_by_mat) assert mat_error < 1e-14, mat_error err = la.norm((bdry_f - bdry_f_2).get(), np.inf) eoc_rec.add_data_point(h, err) order_slack = 0.75 if mesh_name == "blob" else 0.5 print(eoc_rec) assert (eoc_rec.order_estimate() >= order - order_slack or eoc_rec.max_error() < 1e-14)
def get_pseudo_y0_mesh(): """Generate or import a grid using `gmsh`. Input required: data/pseudoY0.brep (for mesh gen) -or- data/pseudoY0.msh (read existing mesh) This routine will generate a new grid if it does not find the grid file (data/pseudoY0.msh), but note that if the grid is generated in millimeters, then the solution initialization and BCs need to be adjusted or the grid needs to be scaled up to meters before being used with the current main driver in this example. """ from meshmode.mesh.io import ( read_gmsh, generate_gmsh, ScriptWithFilesSource ) import os if os.path.exists("data/pseudoY1nozzle.msh") is False: mesh = generate_gmsh( ScriptWithFilesSource(""" Merge "data/pseudoY1nozzle.brep"; Mesh.CharacteristicLengthMin = 1; Mesh.CharacteristicLengthMax = 10; Mesh.ElementOrder = 2; Mesh.CharacteristicLengthExtendFromBoundary = 0; // Inside and end surfaces of nozzle/scramjet Field[1] = Distance; Field[1].NNodesByEdge = 100; Field[1].FacesList = {5,7,8,9,10}; Field[2] = Threshold; Field[2].IField = 1; Field[2].LcMin = 1; Field[2].LcMax = 10; Field[2].DistMin = 0; Field[2].DistMax = 20; // Edges separating surfaces with boundary layer // refinement from those without // (Seems to give a smoother transition) Field[3] = Distance; Field[3].NNodesByEdge = 100; Field[3].EdgesList = {5,10,14,16}; Field[4] = Threshold; Field[4].IField = 3; Field[4].LcMin = 1; Field[4].LcMax = 10; Field[4].DistMin = 0; Field[4].DistMax = 20; // Min of the two sections above Field[5] = Min; Field[5].FieldsList = {2,4}; Background Field = 5; """, ["data/pseudoY1nozzle.brep"]), 3, target_unit="MM") else: mesh = read_gmsh("data/pseudoY1nozzle.msh") return mesh
def test_boundary_interpolation(ctx_factory, group_factory, boundary_tag, 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, 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 = float(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") from meshmode.mesh.io import read_gmsh mesh = read_gmsh( "blob2d-order%d-h%s.msh" % (order, mesh_par), force_ambient_dim=2) 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, group_factory(order)) print("h=%s -> %d elements" % ( h, sum(mgrp.nelements for mgrp in mesh.groups))) x = vol_discr.nodes()[0].with_queue(queue) vol_f = f(x) bdry_connection = make_face_restriction( vol_discr, group_factory(order), boundary_tag, per_face_groups=per_face_groups) check_connection(bdry_connection) bdry_discr = bdry_connection.to_discr bdry_x = bdry_discr.nodes()[0].with_queue(queue) bdry_f = f(bdry_x) bdry_f_2 = bdry_connection(queue, vol_f) if mesh_name == "blob" and dim == 2 and mesh.nelements < 500: mat = bdry_connection.full_resample_matrix(queue).get(queue) bdry_f_2_by_mat = mat.dot(vol_f.get()) mat_error = la.norm(bdry_f_2.get(queue=queue) - bdry_f_2_by_mat) assert mat_error < 1e-14, mat_error err = la.norm((bdry_f-bdry_f_2).get(), np.inf) eoc_rec.add_data_point(h, err) order_slack = 0.75 if mesh_name == "blob" else 0.5 print(eoc_rec) assert ( eoc_rec.order_estimate() >= order-order_slack or eoc_rec.max_error() < 1e-14)
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
wrangler, source_vals * q_weights, source_vals, direct_evaluation=True) zds = pot_direct.get() zs = pot.get() print("P2P-FMM diff =", np.max(np.abs(zs - zds))) print("P2P Error =", np.max(np.abs(ze - zds))) # Write vtk if 0: from meshmode.mesh.io import read_gmsh modemesh = read_gmsh("box_grid.msh", force_ambient_dim=None) from meshmode.discretization.poly_element import ( LegendreGaussLobattoTensorProductGroupFactory, ) from meshmode.discretization import Discretization box_discr = Discretization( ctx, modemesh, LegendreGaussLobattoTensorProductGroupFactory(q_order)) box_nodes_x = box_discr.nodes()[0].with_queue(queue).get() box_nodes_y = box_discr.nodes()[1].with_queue(queue).get() box_nodes_z = box_discr.nodes()[2].with_queue(queue).get() box_nodes = make_obj_array( # get() first for CL compatibility issues [ cl.array.to_device(queue, box_nodes_x), cl.array.to_device(queue, box_nodes_y),
def main(): print("*************************") print("* Setting up...") print("*************************") dim = 3 # download precomputation results for the 3D Laplace kernel download_table = True table_filename = "nft_laplace3d.hdf5" logger.info("Using table cache: " + table_filename) q_order = 7 # quadrature order n_levels = 5 use_multilevel_table = False adaptive_mesh = False n_refinement_loops = 100 refined_n_cells = 5e5 rratio_top = 0.2 rratio_bot = 0.5 dtype = np.float64 m_order = 10 # multipole order force_direct_evaluation = False logger.info("Multipole order = " + str(m_order)) logger.info("Quad order = " + str(q_order)) logger.info("N_levels = " + str(n_levels)) # a solution that is nearly zero at the boundary # exp(-40) = 4.25e-18 alpha = 80 x = pmbl.var("x") y = pmbl.var("y") z = pmbl.var("z") expp = pmbl.var("exp") norm2 = x**2 + y**2 + z**2 source_expr = -(4 * alpha**2 * norm2 - 6 * alpha) * expp(-alpha * norm2) solu_expr = expp(-alpha * norm2) logger.info("Source expr: " + str(source_expr)) logger.info("Solu expr: " + str(solu_expr)) # bounding box a = -0.5 b = 0.5 root_table_source_extent = 2 ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) # logger.info("Summary of params: " + get_param_summary()) source_eval = Eval(dim, source_expr, [x, y, z]) # {{{ generate quad points import volumential.meshgen as mg # Show meshgen info mg.greet() mesh = mg.MeshGen3D(q_order, n_levels, a, b, queue=queue) if not adaptive_mesh: mesh.print_info() q_points = mesh.get_q_points() q_weights = mesh.get_q_weights() else: iloop = -1 while mesh.n_active_cells() < refined_n_cells: iloop += 1 cell_centers = mesh.get_cell_centers() cell_measures = mesh.get_cell_measures() density_vals = source_eval( queue, np.array([[center[d] for center in cell_centers] for d in range(dim)])) crtr = np.abs(cell_measures * density_vals) mesh.update_mesh(crtr, rratio_top, rratio_bot) if iloop > n_refinement_loops: print("Max number of refinement loops reached.") break mesh.print_info() q_points = mesh.get_q_points() q_weights = mesh.get_q_weights() if 1: try: mesh.generate_gmsh("box_grid.msh") except Exception as e: print(e) pass legacy_msh_file = True if legacy_msh_file: import os os.system("gmsh box_grid.msh convert_grid -") assert len(q_points) == len(q_weights) assert q_points.shape[1] == dim q_points = np.ascontiguousarray(np.transpose(q_points)) from pytools.obj_array import make_obj_array q_points = make_obj_array( [cl.array.to_device(queue, q_points[i]) for i in range(dim)]) q_weights = cl.array.to_device(queue, q_weights) # }}} # {{{ discretize the source field logger.info("discretizing source field") source_vals = cl.array.to_device( queue, source_eval(queue, np.array([coords.get() for coords in q_points]))) # particle_weigt = source_val * q_weight # }}} End discretize the source field # {{{ build tree and traversals from boxtree.tools import AXIS_NAMES axis_names = AXIS_NAMES[:dim] from pytools import single_valued coord_dtype = single_valued(coord.dtype for coord in q_points) from boxtree.bounding_box import make_bounding_box_dtype bbox_type, _ = make_bounding_box_dtype(ctx.devices[0], dim, coord_dtype) bbox = np.empty(1, bbox_type) for ax in axis_names: bbox["min_" + ax] = a bbox["max_" + ax] = b # tune max_particles_in_box to reconstruct the mesh # TODO: use points from FieldPlotter are used as target points for better # visuals print("building tree") from boxtree import TreeBuilder tb = TreeBuilder(ctx) tree, _ = tb( queue, particles=q_points, targets=q_points, bbox=bbox, max_particles_in_box=q_order**3 * 8 - 1, kind="adaptive-level-restricted", ) from boxtree.traversal import FMMTraversalBuilder tg = FMMTraversalBuilder(ctx) trav, _ = tg(queue, tree) # }}} End build tree and traversals # {{{ build near field potential table from volumential.table_manager import NearFieldInteractionTableManager import os if download_table and (not os.path.isfile(table_filename)): import json with open("table_urls.json", 'r') as fp: urls = json.load(fp) print("Downloading table from %s" % urls['Laplace3D']) import subprocess subprocess.call(["wget", "-q", urls['Laplace3D'], table_filename]) tm = NearFieldInteractionTableManager(table_filename, root_extent=root_table_source_extent, queue=queue) if use_multilevel_table: logger.info("Using multilevel tables") assert (abs( int((b - a) / root_table_source_extent) * root_table_source_extent - (b - a)) < 1e-15) nftable = [] for lev in range(0, tree.nlevels + 1): print("Getting table at level", lev) tb, _ = tm.get_table( dim, "Laplace", q_order, source_box_level=lev, compute_method="DrosteSum", queue=queue, n_brick_quad_points=120, adaptive_level=False, use_symmetry=True, alpha=0, n_levels=1, ) nftable.append(tb) print("Using table list of length", len(nftable)) else: logger.info("Using single level table") force_recompute = False # 15 levels are sufficient (the inner most brick is 1e-15**3 in volume) nftable, _ = tm.get_table( dim, "Laplace", q_order, force_recompute=force_recompute, compute_method="DrosteSum", queue=queue, n_brick_quad_points=120, adaptive_level=False, use_symmetry=True, alpha=0, n_levels=1, ) # }}} End build near field potential table # {{{ sumpy expansion for laplace kernel from sumpy.expansion import DefaultExpansionFactory from sumpy.kernel import LaplaceKernel knl = LaplaceKernel(dim) out_kernels = [knl] expn_factory = DefaultExpansionFactory() local_expn_class = expn_factory.get_local_expansion_class(knl) mpole_expn_class = expn_factory.get_multipole_expansion_class(knl) exclude_self = True from volumential.expansion_wrangler_fpnd import ( FPNDExpansionWrangler, FPNDExpansionWranglerCodeContainer) wcc = FPNDExpansionWranglerCodeContainer( ctx, partial(mpole_expn_class, knl), partial(local_expn_class, knl), out_kernels, exclude_self=exclude_self, ) if exclude_self: target_to_source = np.arange(tree.ntargets, dtype=np.int32) self_extra_kwargs = {"target_to_source": target_to_source} else: self_extra_kwargs = {} wrangler = FPNDExpansionWrangler( code_container=wcc, queue=queue, tree=tree, near_field_table=nftable, dtype=dtype, fmm_level_to_order=lambda kernel, kernel_args, tree, lev: m_order, quad_order=q_order, self_extra_kwargs=self_extra_kwargs, ) # }}} End sumpy expansion for laplace kernel print("*************************") print("* Performing FMM ...") print("*************************") # {{{ conduct fmm computation from volumential.volume_fmm import drive_volume_fmm import time queue.finish() t0 = time.time() pot, = drive_volume_fmm(trav, wrangler, source_vals * q_weights, source_vals, direct_evaluation=force_direct_evaluation, list1_only=False) t1 = time.time() print("Finished in %.2f seconds." % (t1 - t0)) print("(%e points per second)" % (len(q_weights) / (t1 - t0))) # }}} End conduct fmm computation print("*************************") print("* Postprocessing ...") print("*************************") # {{{ postprocess and plot # print(pot) solu_eval = Eval(dim, solu_expr, [x, y, z]) # x = q_points[0].get() # y = q_points[1].get() # z = q_points[2].get() test_x = np.array([0.0]) test_y = np.array([0.0]) test_z = np.array([0.0]) test_nodes = make_obj_array( # get() first for CL compatibility issues [ cl.array.to_device(queue, test_x), cl.array.to_device(queue, test_y), cl.array.to_device(queue, test_z), ]) from volumential.volume_fmm import interpolate_volume_potential ze = solu_eval(queue, np.array([test_x, test_y, test_z])) zs = interpolate_volume_potential(test_nodes, trav, wrangler, pot).get() print_error = True if print_error: err = np.max(np.abs(ze - zs)) print("Error =", err) # Boxtree if 0: import matplotlib.pyplot as plt if dim == 2: plt.plot(q_points[0].get(), q_points[1].get(), ".") from boxtree.visualization import TreePlotter plotter = TreePlotter(tree.get(queue=queue)) plotter.draw_tree(fill=False, edgecolor="black") # plotter.draw_box_numbers() plotter.set_bounding_box() plt.gca().set_aspect("equal") plt.draw() plt.show() # plt.savefig("tree.png") # Direct p2p if 0: print("Performing P2P") pot_direct, = drive_volume_fmm(trav, wrangler, source_vals * q_weights, source_vals, direct_evaluation=True) zds = pot_direct.get() zs = pot.get() print("P2P-FMM diff =", np.max(np.abs(zs - zds))) print("P2P Error =", np.max(np.abs(ze - zds))) # Write vtk if 0: from meshmode.mesh.io import read_gmsh modemesh = read_gmsh("box_grid.msh", force_ambient_dim=None) from meshmode.discretization.poly_element import ( LegendreGaussLobattoTensorProductGroupFactory, ) from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization actx = PyOpenCLArrayContext(queue) box_discr = Discretization( actx, modemesh, LegendreGaussLobattoTensorProductGroupFactory(q_order)) box_nodes_x = box_discr.nodes()[0].with_queue(queue).get() box_nodes_y = box_discr.nodes()[1].with_queue(queue).get() box_nodes_z = box_discr.nodes()[2].with_queue(queue).get() box_nodes = make_obj_array( # get() first for CL compatibility issues [ cl.array.to_device(queue, box_nodes_x), cl.array.to_device(queue, box_nodes_y), cl.array.to_device(queue, box_nodes_z), ]) visual_order = 1 from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(queue, box_discr, visual_order) from volumential.volume_fmm import interpolate_volume_potential volume_potential = interpolate_volume_potential( box_nodes, trav, wrangler, pot) # qx = q_points[0].get() # qy = q_points[1].get() # qz = q_points[2].get() exact_solution = cl.array.to_device( queue, solu_eval(queue, np.array([box_nodes_x, box_nodes_y, box_nodes_z]))) # clean up the mess def clean_file(filename): import os try: os.remove(filename) except OSError: pass vtu_filename = "laplace3d.vtu" clean_file(vtu_filename) vis.write_vtk_file( vtu_filename, [ ("VolPot", volume_potential), # ("SrcDensity", source_density), ("ExactSol", exact_solution), ("Error", volume_potential - exact_solution), ], ) print("Written file " + vtu_filename)