diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f456bb03..709c7de4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: crate-ci/typos@master + - uses: crate-ci/typos@main docs: name: Documentation diff --git a/boxtree/pyfmmlib_integration.py b/boxtree/pyfmmlib_integration.py index b8bf4901..5ba17162 100644 --- a/boxtree/pyfmmlib_integration.py +++ b/boxtree/pyfmmlib_integration.py @@ -777,10 +777,8 @@ def form_multipoles( formmp = self.tree_indep.get_routine( "%ddformmp" + ("_dp" if self.use_dipoles else ""), suffix="_imany") - except AttributeError: - # pyfmmlib predates the batched (indirect-many) P2M wrappers - return self._form_multipoles_one_box_at_a_time( - level_start_source_box_nrs, source_boxes, src_weights) + except AttributeError as err: + raise RuntimeError("pyfmmlib >= 2026.1 is required") from err mpoles = self.multipole_expansion_zeros() @@ -843,46 +841,6 @@ def form_multipoles( return mpoles - def _form_multipoles_one_box_at_a_time( - self, - level_start_source_box_nrs: Array, - source_boxes: Array, - src_weights: Array) -> Array: - formmp = self.tree_indep.get_routine( - "%ddformmp" + ("_dp" if self.use_dipoles else "")) - - mpoles = self.multipole_expansion_zeros() - for lev in range(self.tree.nlevels): - start, stop = level_start_source_box_nrs[lev:lev+2] - if start == stop: - continue - - level_start_ibox, mpoles_view = self.multipole_expansions_view(mpoles, lev) - rscale = self.level_to_rscale(lev) - - for src_ibox in source_boxes[start:stop]: - pslice = self._get_source_slice(src_ibox) - if pslice.stop - pslice.start == 0: - continue - - kwargs = {} - kwargs.update(self.kernel_kwargs) - kwargs.update(self.get_source_kwargs(src_weights, pslice)) - - ier, mpole = formmp( - rscale=rscale, - source=self._get_sources(pslice), - center=self.tree.box_centers[:, src_ibox], - nterms=self.level_orders[lev], - **kwargs) - - if ier: - raise RuntimeError("formmp failed") - - mpoles_view[src_ibox-level_start_ibox] = mpole.T - - return mpoles - @log_process(logger) @override def coarsen_multipoles( diff --git a/test/test_fmm.py b/test/test_fmm.py index b9b0551f..96d7e20e 100644 --- a/test/test_fmm.py +++ b/test/test_fmm.py @@ -527,74 +527,6 @@ def fmm_level_to_order(tree, lev): # }}} - -@pytest.mark.parametrize("dims", [2, 3]) -@pytest.mark.parametrize("use_dipoles", [True, False]) -@pytest.mark.parametrize("helmholtz_k", [0, 2]) -def test_pyfmmlib_batched_form_multipoles( - actx_factory, dims, use_dipoles, helmholtz_k): - """Check that the batched (indirect-many) P2M path reproduces the - one-box-at-a-time path exactly.""" - pyfmmlib = pytest.importorskip("pyfmmlib") - if not hasattr(pyfmmlib, "l2dformmp_imany"): - pytest.skip("pyfmmlib lacks the batched formmp wrappers") - - actx = actx_factory() - - nsources = 800 - ntargets = 400 - dtype = np.float64 - - sources = p_normal(actx, nsources, dims, dtype, seed=15) - targets = ( - p_normal(actx, ntargets, dims, dtype, seed=18) - + np.array([2, 0, 0])[:dims]) - - from boxtree import TreeBuilder - tb = TreeBuilder(actx) - - tree, _ = tb(actx, sources, targets=targets, - max_particles_in_box=30, debug=True) - - from boxtree.traversal import FMMTraversalBuilder - tbuild = FMMTraversalBuilder(actx) - trav, _ = tbuild(actx, tree, debug=True) - - trav = actx.to_numpy(trav) - - rng = np.random.default_rng(20) - weights = rng.uniform(0.0, 1.0, (nsources,)) - dipole_vec = rng.normal(size=(dims, nsources)) if use_dipoles else None - - from boxtree.pyfmmlib_integration import ( - FMMLibExpansionWrangler, - FMMLibTreeIndependentDataForWrangler, - Kernel, - ) - tree_indep = FMMLibTreeIndependentDataForWrangler( - trav.tree.dimensions, - Kernel.HELMHOLTZ if helmholtz_k else Kernel.LAPLACE) - wrangler = FMMLibExpansionWrangler( - tree_indep, trav, - helmholtz_k=helmholtz_k, - fmm_level_to_order=lambda tree, lev: 10, - dipole_vec=dipole_vec) - - src_weights = wrangler.reorder_sources(weights) - - mpoles_batched = wrangler.form_multipoles( - actx, - trav.level_start_source_box_nrs, - trav.source_boxes, - (src_weights,)) - - mpoles_one_at_a_time = wrangler._form_multipoles_one_box_at_a_time( - trav.level_start_source_box_nrs, - trav.source_boxes, - src_weights) - - assert np.array_equal(mpoles_batched, mpoles_one_at_a_time) - # }}}