def __init__( self, density_discr, fmm_order=False, fmm_level_to_order=None, expansion_factory=None, # begin undocumented arguments # FIXME default debug=False once everything works debug=True): """ :arg fmm_order: `False` for direct calculation. """ LayerPotentialSourceBase.__init__(self, density_discr) self.debug = debug if fmm_order is not False and fmm_level_to_order is not None: raise TypeError( "may not specify both fmm_order and fmm_level_to_order") if fmm_level_to_order is None: if fmm_order is not False: def fmm_level_to_order(kernel, kernel_args, tree, level): # noqa pylint:disable=function-redefined return fmm_order else: fmm_level_to_order = False self.density_discr = density_discr self.fmm_level_to_order = fmm_level_to_order if expansion_factory is None: from sumpy.expansion import DefaultExpansionFactory expansion_factory = DefaultExpansionFactory() self.expansion_factory = expansion_factory
def __init__(self, cl_context, kernel, mpole_expn_class=None, local_expn_class=None, expansion_factory=None, extra_source_kwargs=None, extra_kernel_kwargs=None): self.cl_context = cl_context self.queue = cl.CommandQueue(self.cl_context) self.kernel = kernel self.no_target_deriv_kernel = TargetDerivativeRemover()(kernel) if expansion_factory is None: from sumpy.expansion import DefaultExpansionFactory expansion_factory = DefaultExpansionFactory() if mpole_expn_class is None: mpole_expn_class = \ expansion_factory.get_multipole_expansion_class(kernel) if local_expn_class is None: local_expn_class = \ expansion_factory.get_local_expansion_class(kernel) self.mpole_expn_class = mpole_expn_class self.local_expn_class = local_expn_class if extra_source_kwargs is None: extra_source_kwargs = {} if extra_kernel_kwargs is None: extra_kernel_kwargs = {} self.extra_source_kwargs = extra_source_kwargs self.extra_kernel_kwargs = extra_kernel_kwargs extra_source_and_kernel_kwargs = extra_source_kwargs.copy() extra_source_and_kernel_kwargs.update(extra_kernel_kwargs) self.extra_source_and_kernel_kwargs = extra_source_and_kernel_kwargs
def __init__(self, cl_context, kernel, mpole_expn_class=None, local_expn_class=None, expansion_factory=None, extra_source_kwargs=None, extra_kernel_kwargs=None): self.cl_context = cl_context self.queue = cl.CommandQueue(self.cl_context) self.kernel = kernel self.no_target_deriv_kernel = TargetDerivativeRemover()(kernel) if expansion_factory is None: from sumpy.expansion import DefaultExpansionFactory expansion_factory = DefaultExpansionFactory() if mpole_expn_class is None: mpole_expn_class = \ expansion_factory.get_multipole_expansion_class(kernel) if local_expn_class is None: local_expn_class = \ expansion_factory.get_local_expansion_class(kernel) self.mpole_expn_class = mpole_expn_class self.local_expn_class = local_expn_class if extra_source_kwargs is None: extra_source_kwargs = {} if extra_kernel_kwargs is None: extra_kernel_kwargs = {} self.extra_source_kwargs = extra_source_kwargs self.extra_kernel_kwargs = extra_kernel_kwargs extra_source_and_kernel_kwargs = extra_source_kwargs.copy() extra_source_and_kernel_kwargs.update(extra_kernel_kwargs) self.extra_source_and_kernel_kwargs = extra_source_and_kernel_kwargs
def __init__( self, density_discr, fine_order, qbx_order=None, fmm_order=None, fmm_level_to_order=None, expansion_factory=None, target_association_tolerance=_not_provided, # begin experimental arguments # FIXME default debug=False once everything has matured debug=True, _disable_refinement=False, _expansions_in_tree_have_extent=True, _expansion_stick_out_factor=0.5, _well_sep_is_n_away=2, _max_leaf_refine_weight=None, _box_extent_norm=None, _from_sep_smaller_crit=None, _from_sep_smaller_min_nsources_cumul=None, _tree_kind="adaptive", _use_target_specific_qbx=None, geometry_data_inspector=None, cost_model=None, fmm_backend="sumpy", target_stick_out_factor=_not_provided): """ :arg fine_order: The total degree to which the (upsampled) underlying quadrature is exact. :arg fmm_order: `False` for direct calculation. May not be given if *fmm_level_to_order* is given. :arg fmm_level_to_order: A function that takes arguments of *(kernel, kernel_args, tree, level)* and returns the expansion order to be used on a given *level* of *tree* with *kernel*, where *kernel* is the :class:`sumpy.kernel.Kernel` being evaluated, and *kernel_args* is a set of *(key, value)* tuples with evaluated kernel arguments. May not be given if *fmm_order* is given. Experimental arguments without a promise of forward compatibility: :arg _use_target_specific_qbx: Whether to use target-specific acceleration by default if possible. *None* means "use if possible". :arg cost_model: Either *None* or an object implementing the :class:`~pytential.qbx.cost.AbstractQBXCostModel` interface, used for gathering modeled costs if provided (experimental) """ # {{{ argument processing if fine_order is None: raise ValueError("fine_order must be provided.") if qbx_order is None: raise ValueError("qbx_order must be provided.") if target_stick_out_factor is not _not_provided: from warnings import warn warn( "target_stick_out_factor has been renamed to " "target_association_tolerance. " "Using target_stick_out_factor is deprecated " "and will stop working in 2018.", DeprecationWarning, stacklevel=2) if target_association_tolerance is not _not_provided: raise TypeError( "May not pass both target_association_tolerance and " "target_stick_out_factor.") target_association_tolerance = target_stick_out_factor del target_stick_out_factor if target_association_tolerance is _not_provided: target_association_tolerance = float( np.finfo(density_discr.real_dtype).eps) * 1e3 if fmm_order is not None and fmm_level_to_order is not None: raise TypeError( "may not specify both fmm_order and fmm_level_to_order") if _box_extent_norm is None: _box_extent_norm = "l2" if _from_sep_smaller_crit is None: # This seems to win no matter what the box extent norm is # https://gitlab.tiker.net/papers/2017-qbx-fmm-3d/issues/10 _from_sep_smaller_crit = "precise_linf" if fmm_level_to_order is None: if fmm_order is False: fmm_level_to_order = False else: def fmm_level_to_order(kernel, kernel_args, tree, level): # noqa pylint:disable=function-redefined return fmm_order if _max_leaf_refine_weight is None: if density_discr.ambient_dim == 2: # FIXME: This should be verified now that l^2 is the default. _max_leaf_refine_weight = 64 elif density_discr.ambient_dim == 3: # For static_linf/linf: https://gitlab.tiker.net/papers/2017-qbx-fmm-3d/issues/8#note_25009 # noqa # For static_l2/l2: https://gitlab.tiker.net/papers/2017-qbx-fmm-3d/issues/12 # noqa _max_leaf_refine_weight = 512 else: # Just guessing... _max_leaf_refine_weight = 64 if _from_sep_smaller_min_nsources_cumul is None: # See here for the comment thread that led to these defaults: # https://gitlab.tiker.net/inducer/boxtree/merge_requests/28#note_18661 if density_discr.dim == 1: _from_sep_smaller_min_nsources_cumul = 15 else: _from_sep_smaller_min_nsources_cumul = 30 # }}} LayerPotentialSourceBase.__init__(self, density_discr) self.fine_order = fine_order self.qbx_order = qbx_order self.fmm_level_to_order = fmm_level_to_order assert target_association_tolerance is not None self.target_association_tolerance = target_association_tolerance self.fmm_backend = fmm_backend if expansion_factory is None: from sumpy.expansion import DefaultExpansionFactory expansion_factory = DefaultExpansionFactory() self.expansion_factory = expansion_factory self.debug = debug self._disable_refinement = _disable_refinement self._expansions_in_tree_have_extent = \ _expansions_in_tree_have_extent self._expansion_stick_out_factor = _expansion_stick_out_factor self._well_sep_is_n_away = _well_sep_is_n_away self._max_leaf_refine_weight = _max_leaf_refine_weight self._box_extent_norm = _box_extent_norm self._from_sep_smaller_crit = _from_sep_smaller_crit self._from_sep_smaller_min_nsources_cumul = \ _from_sep_smaller_min_nsources_cumul self._tree_kind = _tree_kind self._use_target_specific_qbx = _use_target_specific_qbx self.geometry_data_inspector = geometry_data_inspector if cost_model is None: from pytential.qbx.cost import QBXCostModel cost_model = QBXCostModel() self.cost_model = cost_model
def main(): print("*************************") print("* Setting up...") print("*************************") dim = 2 # download precomputation results for the 2D Laplace kernel download_table = True table_filename = "nft_laplace2d.hdf5" root_table_source_extent = 2 print("Using table cache:", table_filename) q_order = 9 # quadrature order n_levels = 6 # 2^(n_levels-1) subintervals in 1D use_multilevel_table = False adaptive_mesh = False n_refinement_loops = 100 refined_n_cells = 2000 rratio_top = 0.2 rratio_bot = 0.5 dtype = np.float64 m_order = 20 # multipole order force_direct_evaluation = False print("Multipole order =", m_order) alpha = 160 x = pmbl.var("x") y = pmbl.var("y") expp = pmbl.var("exp") norm2 = x**2 + y**2 source_expr = -(4 * alpha**2 * norm2 - 4 * 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) source_eval = Eval(dim, source_expr, [x, y]) # {{{ generate quad points import volumential.meshgen as mg # Show meshgen info mg.greet() mesh = mg.MeshGen2D(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 crtr = np.abs( source_eval(mesh.get_cell_centers) * mesh.get_cell_measures) 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() 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) # q_radii = cl.array.to_device(queue, q_radii) # }}} # {{{ discretize the 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 from boxtree import TreeBuilder tb = TreeBuilder(ctx) tree, _ = tb( queue, particles=q_points, targets=q_points, bbox=bbox, max_particles_in_box=q_order**2 * 4 - 1, kind="adaptive-level-restricted", ) bbox2 = np.array([[a, b], [a, b]]) tree2, _ = tb( queue, particles=q_points, targets=q_points, bbox=bbox2, max_particles_in_box=q_order**2 * 4 - 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['Laplace2D']) import subprocess subprocess.call(["wget", "-q", urls['Laplace2D'], table_filename]) tm = NearFieldInteractionTableManager(table_filename, root_extent=root_table_source_extent, queue=queue) if use_multilevel_table: 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=100, adaptive_level=False, use_symmetry=True, alpha=0.1, nlevels=15, ) nftable.append(tb) print("Using table list of length", len(nftable)) else: nftable, _ = tm.get_table( dim, "Laplace", q_order, force_recompute=False, compute_method="DrosteSum", queue=queue, n_brick_quad_points=100, adaptive_level=False, use_symmetry=True, alpha=0.1, nlevels=15, ) # }}} 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 ( FPNDExpansionWranglerCodeContainer, FPNDExpansionWrangler) 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, ) queue.finish() 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]) x = q_points[0].get() y = q_points[1].get() ze = solu_eval(queue, np.array([x, y])) zs = pot.get() print_error = True if print_error: err = np.max(np.abs(ze - zs)) print("Error =", err) # Interpolated surface if 0: h = 0.005 out_x = np.arange(a, b + h, h) out_y = np.arange(a, b + h, h) oxx, oyy = np.meshgrid(out_x, out_y) out_targets = make_obj_array([ cl.array.to_device(queue, oxx.flatten()), cl.array.to_device(queue, oyy.flatten()), ]) from volumential.volume_fmm import interpolate_volume_potential # src = source_field([q.get() for q in q_points]) # src = cl.array.to_device(queue, src) interp_pot = interpolate_volume_potential(out_targets, trav, wrangler, pot) opot = interp_pot.get() import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D plt3d = plt.figure() ax = Axes3D(plt3d) # noqa surf = ax.plot_surface(oxx, oyy, opot.reshape(oxx.shape)) # noqa # ax.scatter(x, y, src.get()) # ax.set_zlim(-0.25, 0.25) plt.draw() plt.show() # Boxtree if 0: import matplotlib.pyplot as plt if dim == 2: # plt.plot(q_points[0].get(), q_points[1].get(), ".") pass 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))) """ import matplotlib.pyplot as plt import matplotlib.cm as cm x = q_points[0].get() y = q_points[1].get() plt.scatter(x, y, c=np.log(abs(zs-zds)) / np.log(10), cmap=cm.jet) plt.colorbar() plt.xlabel("Multipole order = " + str(m_order)) plt.draw() plt.show() """ # Scatter plot if 0: import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D x = q_points[0].get() y = q_points[1].get() ze = solu_eval(queue, np.array([x, y])) zs = pot.get() plt3d = plt.figure() ax = Axes3D(plt3d) ax.scatter(x, y, zs, s=1) # ax.scatter(x, y, source_field([q.get() for q in q_points]), s=1) # import matplotlib.cm as cm # ax.scatter(x, y, zs, c=np.log(abs(zs-zds)), cmap=cm.jet) # plt.gca().set_aspect("equal") # ax.set_xlim3d([-1, 1]) # ax.set_ylim3d([-1, 1]) # ax.set_zlim3d([np.min(z), np.max(z)]) # ax.set_zlim3d([-0.002, 0.00]) plt.draw() plt.show()
tb_dx.integral_knl.__repr__(): tb_dx, tb_dy.integral_knl.__repr__(): tb_dy, } # }}} End build near field potential table # {{{ sumpy expansion for laplace kernel from sumpy.expansion import DefaultExpansionFactory from sumpy.kernel import LaplaceKernel, AxisTargetDerivative knl = LaplaceKernel(dim) knl_dx = AxisTargetDerivative(0, knl) knl_dy = AxisTargetDerivative(1, knl) expn_factory = DefaultExpansionFactory() local_expn_class = expn_factory.get_local_expansion_class(knl) mpole_expn_class = expn_factory.get_multipole_expansion_class(knl) out_kernels = [knl, knl_dx, knl_dy] exclude_self = True from volumential.expansion_wrangler_fpnd import ( FPNDExpansionWranglerCodeContainer, FPNDExpansionWrangler) wcc = FPNDExpansionWranglerCodeContainer( ctx, partial(mpole_expn_class, knl), partial(local_expn_class, knl), out_kernels, exclude_self=exclude_self, )
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)