Use pyfmmlib's batched formmp wrappers in form_multipoles - #124
Merged
Conversation
pyfmmlib now generates {l,h}{2,3}dformmp[_dp]_imany (inducer/pyfmmlib#94),
the P2M twins of the formta wrappers that form_locals already uses. Switch
form_multipoles to one indirect-many call per level, with each expansion
center drawing on the single source segment of its own box; boxes without
sources are excluded so their expansion entries stay zero, as before.
When pyfmmlib predates these wrappers, form_multipoles transparently falls
back to the previous one-box-at-a-time loop, which is kept verbatim as
_form_multipoles_one_box_at_a_time.
The batched path reproduces the loop bit for bit (tested across 2D/3D,
charges/dipoles, Laplace/Helmholtz). On a 100k-source 3D Laplace tree with
7216 source boxes at order 15, form_multipoles drops from 516 ms to 236 ms
on a 4C/8T laptop with OpenMP, and from 623 ms to 561 ms single-threaded
(the call-overhead saving alone).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYp74e5ZTE6ZHDNR3ETgM6
FMMLibExpansionWrangler now declares tree_indep as its concrete FMMLibTreeIndependentDataForWrangler, which is what every get_routine call site already assumes; without the narrowing, basedpyright flags the batched-P2M probe (the guarded call inside try/except AttributeError) as accessing an attribute the base class lacks. The runtime AttributeError that the guard catches comes from pyfmmlib lacking the generated wrapper, not from the method lookup itself. Also materialize box_source_counts_nonchild through np.asarray before the emptiness comparison so the operator is typed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YYp74e5ZTE6ZHDNR3ETgM6
inducer
enabled auto-merge (rebase)
August 5, 2026 13:25
Owner
|
Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to inducer/pyfmmlib#94, as discussed there.
What
form_multipolescurrently forms one multipole per scalarformmpcall in a Python loop over source boxes. With pyfmmlib now generating{l,h}{2,3}dformmp[_dp]_imany, this PR switches it to one indirect-many call per level — the same addressing patternform_localsalready uses withformta_imany, with each expansion center drawing on the single source segment of its own box. Boxes without sources are excluded from the batched call so their expansion entries stay zero, exactly as the loop behaved.If pyfmmlib predates the wrappers (
AttributeErrorfrom the routine lookup),form_multipolestransparently falls back to the previous loop, kept verbatim as_form_multipoles_one_box_at_a_time— so nothing changes for released pyfmmlib until its next release, and no requirement bump is needed.Verification
test_pyfmmlib_batched_form_multipolespins bit-exact equality between the batched path and the one-box-at-a-time path across 2D/3D × charges/dipoles × Laplace/Helmholtz (8 parametrizations, all passing). It skips when pyfmmlib lacks the wrappers.test_pyfmmlib_fmmandtest_pyfmmlib_numerical_stabilitypass through the new path (12 tests)._imanylookup raises.Measured
100k-source 3D Laplace tree, 7216 source boxes, order 15, 4C/8T laptop (pyfmmlib built with OpenMP per inducer/pyfmmlib#93):
form_multipoles516 ms → 236 ms. Single-threaded (OMP_NUM_THREADS=1): 623 ms → 561 ms, the call-overhead saving alone.🤖 Generated with Claude Code
https://claude.ai/code/session_01YYp74e5ZTE6ZHDNR3ETgM6