Skip to content

Add pytests, numba optimizations, and switching between ne2001p/ne2025#3

Open
telegraphic wants to merge 28 commits intostella-ocker:mainfrom
telegraphic:dcp_dev
Open

Add pytests, numba optimizations, and switching between ne2001p/ne2025#3
telegraphic wants to merge 28 commits intostella-ocker:mainfrom
telegraphic:dcp_dev

Conversation

@telegraphic
Copy link
Copy Markdown

@telegraphic telegraphic commented Feb 21, 2026

Hi @stella-ocker, I'd like to update pygedm to provide access to NE2025, including on the web app at https://apps.datacentral.org.au/pygedm/. FYI the work to update pygedm is happening here: FRBs/pygedm#30

As a first port of call, I added references to the NE2025 paper to the pygedm README.md. (I note users have been great at citing the underlying models when publishing work using pygedm).

I wanted to generate some all-sky DM maps for the web app, but the 45x slowdown made it unfeasible. So I did some optimizations, adding numba in hotspots, and got a 4-5x speedup.

Before I started the optimizations, I wanted to make sure I didn't break anything, so added some unit tests and regression tests. I copied the test code in the __main__ functions as the baseline.

I also wanted to enable switching between NE2001p and NE2025 without reimporting mwprop. I've added a set_model() function to do this, which required some rejigging (using importlib)

I realise this is a huge PR! So I won't be offended if it doesn't suit your plans and you don't wish to merge it.

- Create tests/test_ne2025_main.py: Tests for NE2025 help/explain functionality
- Create tests/test_ne2001_main.py: Tests for NE2001 help/explain functionality
- Create tests/test_ne_input_main.py: Tests for read_nemod_parameters() function
- Create tests/test_iss_mw_utils_main.py: Tests for scattering calculations
- Existing tests/test_dmdsm.py: Tests for dmdsm_dm2d() with plotting enabled
- Configure pytest with output directory fixture for plot files
- Add .coveragerc for code coverage configuration
- Add pytest-cov to environment.yaml dependencies
- All 16 tests pass with code coverage tracking
sech²(z) = 4·exp(-2|z|) / (1 + exp(-2|z|))²
This avoids np.cosh overflow warnings for large |z|
Applied Numba @njit to hottest functions in LoS integration:
- nevoidN: Inner loop over void array (~589 calls per dmdsm_dm2d)
- ne_outer/ne_inner: Disk component calculations (~40 calls per pass)

JIT compilation automatically handles:
- Loop unrolling and LLVM optimization
- Elimination of Python function call overhead
- Native machine code generation for math operations

Performance gains (cumulative from session start):
- Initial baseline:           ~0.052s per dmdsm_dm2d call
- After cumulative_trapezoid: ~0.035s (1.49× speedup)
- After preallocation+cache:  ~0.028s (1.86× speedup)
- After Numba JIT (current):  ~0.011s (4.65× total speedup)

Speedup breakdown:
- nevoidN:         ~2.0-2.5× (inner void loop now compiled)
- ne_outer/inner:  ~1.5-2.0× (math operations optimized)
- Overall:         ~1.5-2.0× (amortized after first JIT compilation)

First-call overhead: ~0.40s (JIT compFirst-call overhead: ~0.40s (JIT compFirst-call overhead: ~0.40s (JIT compFirst-call overhead: ~0.40s (JIT compFirst-→First-call overhead: ~0.40s (JIT compFirst-call overhead: ~baFirst-call overhead: ~0.40s (JIT compFirst-call overhead: ~0.40ba version: 0.64.0, llvmlite 0.46.0
- All regression tests passing (8/8 sm- All regression tests passing (8/8 sss- All regression tests passing (8/8 snc- All regression tests passing (8/8 sm- All rct- All regression tests passing (8/8 sm- All regressed - All regression tests passing (8/8 sm- All regre2)- All regression tests passing (8/8 sm- All regression tl,- All regression tests passing (8/8 sm- All regressioon- All regression tests passing (8/8 sm- All regressioests passing
…p achieved

Added comprehensive documentation of two-phase optimization:

PHASE 1: Algorithmic improvements
- cumulative_trapezoid: O(N²) → O(N) integration (1.49× speedup)
- Array preallocation: Removed append-in-loop overhead (1.86× cumulative)
- Parameter caching: Reduced dict lookups in hot path

PHASE 2: Numba JIT compilation
- nevoidN: Inner void loop compiled (589 calls)
- ne_outer/ne_inner: Density functions compiled (40 calls)
- Graceful fallback if numba unavailable
- First-call JIT overhead: ~0.40s (amortized over typical runs)

TOTAL RESULT: 4.7× speedup
- Baseline: 0.052s per dmdsm_dm2d call
- Current: 0.011s per call (78.7% faster)
- Typical use (1+100 calls): 14.9ms average

TESTING STATUS:
✅ test_ne2001_main.py: 3/3 passing
✅ smooth_components_regression.py✅ smooth_components_regression.py✅ smooth_components_regression.py✅ smooth_components_regression.py✅ smooth_components_regression.py✅ smooth_components_regression.py✅ smooth_components_regression.py✅ smooth_components_regressiSpline construction overhead in spiral arm lookups
- Future: JIT ne_lism functions (needs careful v- Future: JIT ne_lism functions (needs careful v- Future: JIT ne_litput checks.
@telegraphic telegraphic changed the title Create basic pytest tests, using existing tests in __main__ blocks Add pytest tests, some optimizations, and switching between ne2001p/ne2025 Feb 23, 2026
@telegraphic telegraphic changed the title Add pytest tests, some optimizations, and switching between ne2001p/ne2025 Add pytests, numba optimizations, and switching between ne2001p/ne2025 Feb 23, 2026
@telegraphic
Copy link
Copy Markdown
Author

Update: code now runs significantly faster, mainly due to use of cumulative_trapezoid. It turns out this:

    dm_cumulate_vec = \
        pc_in_kpc * array([trapz(ne[:j], sf_vec[:j]) for j in range(1, Ns_fine+1) ])

is much slower than

    dm_cumulate_vec = pc_in_kpc * cumulative_trapezoid(ne, sf_vec, initial=0.0)

which is also neater.

Vectorizing density_2001_smooth_comps_vec (i.e. removing the for loop) also gave a bump.

When integrating out to 50 kpc, it's now running about 115x faster (3231 ms -> 28 ms).

@telegraphic
Copy link
Copy Markdown
Author

With these speed-ups, I was able to generate some all-sky maps of DM out to different distances on my laptop in a few hours. Here's a pretty picture of the NE2025 estimate for DM out to the galactic center:

screenshot_v4 0 0

@telegraphic
Copy link
Copy Markdown
Author

And example comparison between models:

image

@stella-ocker
Copy link
Copy Markdown
Owner

Hey @telegraphic the updated pygedm interface looks awesome. Really good to know about the cumulative_trapezoid speedup, sounds worth including in mwprop. Given the magnitude of this PR (and some of the specificity to your update of pygedm) I won't merge the whole thing, but I'll make sure to note which future updates are drawn from this fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants