Improve performance with block FFT and iterative solver - #46
Draft
Alexboiboi wants to merge 26 commits into
Draft
Conversation
- Added `demag_fft.py` for FFT-based matvec operations on uniform Cartesian cuboid grids, leveraging the Newell formula for demagnetization. - Introduced `newell.py` to compute the analytical demagnetization tensor for rectangular prisms, including auxiliary functions and self-demagnetization factors. - Created performance tests in `test_demag_performance.py` to validate the accuracy and efficiency of the FFT solver against direct methods. - Developed unit tests in `test_newell.py` to ensure correctness of the Newell tensor calculations and their integration with existing functionality. Co-authored-by: Copilot <copilot@github.com>
…tization calculations Co-authored-by: Copilot <copilot@github.com>
…th sparse matrices Co-authored-by: Copilot <copilot@github.com>
…response into feat/improve-performance
…ed coverage Both solvers now share one interaction model and agree to solver_tol for any input (verified: mixed bodies, rotations, anisotropy, 180-deg groups): - newell: generalized volume-averaged tensor for different-size parallel prisms (4-point axis differences, observer-volume normalization, exact volume-weighted reciprocity), validated against Gauss-Legendre volume averages of magpylib's exact cuboid field - demag_fft: analyze_structure partitions cells into grid/loose/generic clusters covering every cell exactly once (fixes silently dropped cells, 92% error); stable quaternion canonicalization (fixes 180-deg splits); grid spacing decoupled from cell size (gapped grids FFT-eligible); per-component grid detection (two identical meshed bodies = two grids) - demag: single pair rule (parallel cuboids -> Newell, else point-matched getH) evaluated dense for solver="direct" and matrix-free for solver="iterative"; solve always in the global frame (fixes anisotropic susceptibility under rotation, 5.5% error); chi.V/r^3 triage replaced by exact per-row dropped-sum budget (rigorous 0.1*solver_tol bound); GMRES non-convergence now raises; reciprocity transpose + displacement dedup cut build costs (direct assembly ~6x faster, n=27000 iterative in ~2s) - fix latent write-back misalignment (sources_all vs magnets_list) and restore polarizations mutated during getH tensor evaluation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mple - demag_fft.analyze_collection(sources): collection-level structure analysis (extracts positions/dims/rotations/cuboid mask); demag.py and the docs example now share it instead of duplicating the plumbing - solver_performance example: warm-up both solvers before timing (first call was inflating the reference ~10x), reuse the meshed collection for the traced memory run (halves meshing, removes mesh allocations from peak), robust normalization baseline, harmonized crossover thresholds, cylinder-cell prose fix, single-run timing caveat - correct dense-matrix figure at N=27000: ~50 GB, in example and method.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… solution Meshes a spherical TriangularMesh into Tetrahedron cells via TetGen, shows the cells take the point-matched generic path, and reproduces the analytical soft-sphere polarization J0/(1+chi/3) to 0.01%. Adds the tetgen extra to the docs dependency group so the page executes on RTD. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plotly sphinx_gallery renderer embeds MathJax v2 synchronously in the page body; it clobbers window.MathJax before Sphinx's deferred MathJax v3 runs, so math never typeset on any page containing a chart. Strip the injected v2 tags in an html-page-context hook (no TeX is used in chart labels; PlotlyConfig.MathJaxConfig='local' already isolates plotly). Also set nb_execution_timeout=120 for benchmark cells on slow builders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_collection_from_obj_and_cells copied susceptibility to the mesh cells but silently dropped H_ext, so an external field set on a magnet was ignored after meshing. Both material attributes now carry over; add a regression test (high-chi soft cube in a 0.1 T field). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion - quickstart: executed minimal workflow plus the input conventions (susceptibility attribute vs kwarg precedence, anisotropic values, H_ext in Tesla units) - API reference page via autodoc for all public functions - README: installation (pip/conda/[tetgen] extra), key-features list, fixed warning callout - navigation: Getting started / Reference toctrees, examples index with descriptions in reading order, changelog page wired into the site Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- newell: demag_tensor_newell docstring said the mu_0 division was not applied when it is — corrected wording - method.md: working source links on the rendered site, sign-convention section now describes the default block assembly (legacy 4-index layout noted separately), correct Chadebec 2006 citation, and coverage of the cluster size gates, GMRES restart/warm-start semantics, current-source RHS contribution, conditional reciprocity/dedup, and the legacy branch - examples: soft_magnets plots distance in meters instead of a raw path index, magpylib>=5 guard added, unused json imports removed, SI-unit comments and cross-links to the solver-performance page - demag: drop stale TODOs (currents and H_ext are implemented) - demag_fft: harmonize British spellings; conf.py: dynamic copyright year Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Correctness (each with a regression test): - 3-magnet collections with per-object scalar susceptibilities were misread as one anisotropic 3-vector (hierarchy values are per-source) - legacy split crashed on single-source subsets (getH squeezes) - max_dist>0 crashed on entry (unpacked 4 values from a 3-tuple) and, with pairs_matching, used magpylib's deprecated functional string interface — migrated to magpy.func.cuboid_field - match_pairs keyed pairs on dimension DIFFERENCES (colliding distinct geometries) with absolute 1e-8 rounding (collapsing sub-1e-8 m meshes); now absolute source dims + scale-relative rounding - serialization dropped H_ext, Collection-level susceptibility, and crashed on numpy-typed values - meshing edge crashes: thin-segment nphi/nh and voxelize grid counts clamped; empty voxel grids raise a clear error - H_ext docstrings now state the Tesla (B-field) convention the code implements; dead H_ext kwarg removed from get_H_ext Performance: - timelog exit blocked up to min_log_time/5 per block (sleep+join); Event.wait makes it instant — a 27-cell solve drops from ~0.5 s to ~5 ms and the test suite runs 5x faster - direct solver assembles Q in place (T *= -chi; diag += 1; LAPACK overwrite), cutting peak memory ~2-3x Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New example driving the material response with current sources: a three-cuboid U-core with a multi-turn coil built from the polyline helpers (filleted square loop, winding-pack grid swept along it), the field mapped on a plane above the poles, and a cuboid- vs tetrahedral-mesh comparison at three refinement levels with solve times in the plot legends. Also documents the two public polyline functions on the API page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- move the field plane to 30 mm above the poles and double its resolution (161x81, 1 mm step); show it semi-transparent in the scene - compare cuboid vs tetrahedral meshes at matched refinement levels up to target_elems=1000, with solve times in the legend and adaptive Blues/Oranges shade ramps (hue = cell type, darker = finer); halve the tet target since TetGen's quality refinement ~doubles the requested count - evaluate the comparison on the y=0 line only to keep getB cheap - replace the trimesh-vs-tetmesh figure with a side-by-side of the finest demagnetized cuboid and tet systems (magnetization coloring) - state that the remaining gap is discretization error: both families converge to the same limit, cuboids ~10% low at 1000 cells because the corner-concentrated magnetization is under-resolved - exclude jupytext-paired *.ipynb from the sphinx build; they shadowed the .md source and shipped stale outputs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d documentation accuracy
Bugs:
- remove stray $$ at the end of method.md references
- fix reST :func: role to MyST {func} syntax in method.md
- drop brittle magpylib major-version checks from the two older
examples (pyproject already enforces magpylib>=5.0)
- correct stale 'field map' wording in the U-core intro
API coverage:
- document voxelize, trimesh_from_model3d, and the legacy
filter_distance/match_pairs helpers on the API page
- document anisotropic susceptibility consistently: accepted shapes in
the apply_demag docstring, global-frame convention in method.md and
quickstart
Polish:
- rewrite the padded intros of the two oldest examples; retitle
'Cuboids demagnetization' to 'Cuboid Demagnetization'
- README: minimal runnable example + current-sources feature bullet
- quickstart: current sources as drive term, link U-core example
- cross-link tetrahedral example to the U-core mesh comparison
- serialization: drop duplicate import, list supported types
- clarify FEM template-dataframe comments, unify magpy.show usage,
remove empty module index from the landing page
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- _data: import importlib.resources explicitly — get_dataset relied on another package having imported the submodule first (latent AttributeError in a clean import graph) - demag: accept 0-d numpy arrays as scalar susceptibility instead of raising 'len() of unsized object'; add regression test - polyline: correct max_ratio docstring default (1, not 0.5) - meshing_utils: get_volume docstring says m³ (SI), not mm³ - meshing: voxelize docstring parameter name strict_inside Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- demag_tensor: document that point matching leaves None-polarization sources with the last unit polarization (apply_demag unaffected) - meshing_utils: make Sphere and CylinderSegment inside-masks use tolerances relative to the object size (scale-invariant, consistent with Cuboid/Cylinder); document local-frame convention on mask_inside - trimesh_from_model3d: vertex dedup quantization relative to geometry scale (absolute 1e-10 rounding collapsed sub-1e-10 m shapes), keep original coordinates, single np.unique pass; add regression test - fix stray quote in ElapsedTimeThread docstring; add get_dataset docstring (renders on the API page) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- _assemble_T_dense: derive reverse Newell cross-blocks by volume-weighted reciprocity transpose instead of a second analytical evaluation, matching the iterative operator build. 12-body/1500-cell direct solve: 2.03 s -> 1.21 s, result identical to 3e-13 relative. - timelog: skip the 'Starting:' watchdog thread while package logging is silent (the default) — ~1 µs per step instead of a thread spawn+join. The loguru activation probe falls back to the previous always-spawn behaviour on any surprise, and a test pins the loguru-internal contract (activation names carry a trailing dot). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
to_json/from_json now cover the full magpylib magnet family, so demagnetized tetrahedral collections from mesh_TriangularMesh can be persisted and reloaded as drop-in field sources. Includes an end-to-end test of the mesh -> apply_demag -> JSON round trip and updated serialization docs/changelog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
No description provided.