This release adds truncated NFW and Hernquist potentials, time-dependent SCFPotentials built from functions, densities, or N-body snapshots, variational (integrate_dxdv) integration with the symplectic integrators and for 1D orbits, and a smooth P-spline reconstruction of stream tracks, along with many accuracy improvements (several of which change numerical results, e.g., actionAngleStaeckel and actionAngleAdiabatic with c=True and velocity sampling from spherical distribution functions; see HISTORY.txt), bug fixes, speed-ups, wheels for Python 3.15, and dependency/project maintenance updates.
Major changes
-
TruncatedNFWPotential and TruncatedHernquistPotential (Baltz, Marshall & Oguri 2009) by @jobovy-agent in #1743:
The new
TruncatedNFWPotentialandTruncatedHernquistPotentialare the NFW and Hernquist profiles with the smooth truncation of Baltz, Marshall & Oguri (2009),(rc^2 / (rc^2 + r^2))^n, with closed-form mass, potential, forces, and second derivatives accurate to ~1e-14. The truncation radius can depend on time, for example to model a satellite that is stripped from the outside in as thepotof aMovingObjectPotential. Both have C implementations and can be used with the spherical distribution functions. -
Add time-dependent support to SCFPotential by @jobovy with Claude in #1058
-
Add SCFPotential.from_nbody for N-body / particle initialization by @jobovy with Claude in #1061
-
Translate between SCFPotential and MultipoleExpansionPotential by @jobovy with Claude in #1062:
SCFPotentialcan now be time-dependent: its expansion coefficients can be functions of time, or be sampled on a time grid, and are interpolated with a cubic spline, with fast evaluation in Python and C;SCFPotential.from_densitybuilds one from a time-dependent density. The newSCFPotential.from_nbodybuilds an expansion directly from particle positions and masses (a time-dependent one from multiple snapshots), andSCFPotential.from_multipoleandMultipoleExpansionPotential.from_scftranslate between the SCF and multipole representations. -
Symplectic C integrators: variational (dxdv) integration — leapfrog_c/symplec4_c/symplec6_c differentiable by @jobovy with Claude in #1086
-
1D (linear-orbit) variational (dxdv) integration — RK + symplectic by @jobovy with Claude in #1091
-
doc: symplectic and 1D (linear) dxdv examples in the phase-space/chaos tutorial by @jobovy with Claude in #1093:
Orbit.integrate_dxdvnow works with the symplectic C integrators (leapfrog_c,symplec4_c,symplec6_c), which propagate the deviations with the exact tangent map of each step, so that the state-transition matrix is symplectic to machine precision.integrate_dxdvalso now supports 1D orbits, with both the Runge-Kutta and the symplectic integrators. -
streamTrack: a smooth P-spline reconstruction (method="pspline") as the default by @jobovy-agent in #1732:
streamspraydf.streamTrackandStreamTrack.from_particlesnow reconstruct the track by default with a penalized-spline fit of the particles' offsets from the progenitor's orbit, with each coordinate's smoothing chosen by restricted maximum likelihood. The track is now a smooth function of the particles and the progenitor, and for large samples the fit takes about half the time and a quarter of the memory. The previous reconstruction remains available asmethod='binned'.
New Features
- isConservative: symplectic integration for forces that derive from a potential, including a frame that only accelerates by @jobovy-agent in #1748
- linearPotential: x2deriv and evaluatelinearx2derivs, the second derivative of 1D potentials by @jobovy-agent in #1519
- actionAngleVerticalInverse: exact (ODE-based) point transformation by @jobovy-agent in #1309
- Integrate the 1D exact point transformation in the auxiliary angle by @jobovy-agent in #1320
- Exact 1D point transformation by quadrature instead of ODE integration by @jobovy-agent in #1351
- Unit support for actionAngleVerticalInverse by @jobovy-agent in #1342
- actionAngleVerticalInverse: the momentum-matched canonical map by @jobovy-agent in #1415
- actionAngleVerticalInverse: speed up the momentum-matched evaluation (Newton on the closed-form derivative) and construction by @jobovy-agent in #1515
- actionAngleVerticalInverse: the map's action derivatives on each torus, stored as Hermite constraints of the family by @jobovy-agent in #1517
- OblateStaeckelWrapperPotential: optional tabulated C evaluation (ntab=), ~10-16x faster C integration by @jobovy-agent in #1468
- actionAngleStaeckel: the focal length from a potential that supplies one by @jobovy-agent in #1560
- PerfectEllipsoidPotential: the focal length of an axisymmetric oblate perfect ellipsoid by @jobovy-agent in #1561
- dehnendf/shudf moments: accept a numpy array of radii by @jobovy-agent in #1651
- SphericalShell/HomogeneousSphere: accept array input; numpy.infty -> numpy.inf by @jobovy-agent in #1324
- Orbit.from_name: fall back on the other SIMBAD mirrors when one cannot be reached by @jobovy-agent in #1770
- Add Python 3.15 wheels by @jobovy with Claude in #1583
- Build Pyodide wheels for Python 3.14 by @jobovy in #1590
Speed-ups
- Speed up ChandrasekharDynamicalFrictionForce construction ~100x for array-capable potentials by @jobovy-agent in #1422
- ChandrasekharDynamicalFrictionForce: the force cache has never hit (2.1x on the friction path) by @jobovy-agent in #1484
- Do not fork a worker per task when numcores=1 (Orbit.lyapunov 94x faster) by @jobovy-agent in #1458
- actionAngleIsochroneInverse: solve Kepler's equation for all angles at once by @jobovy-agent in #1405
- interpRZPotential: build the sampling grids with one vectorised call by @jobovy-agent in #1294
- Orbit: analytic e/rperi/rap/zmax no longer rebuild the spherical-fallback actionAngle on every call by @jobovy-agent in #1660
- actionAngleStaeckel (c=False): chi-anomaly quadrature mesh matches C (1.6x faster, same accuracy) by @jobovy-agent in #1644
Accuracy improvements
- actionAngleStaeckel: regularize the action quadratures' sqrt turning points (C + Python) by @jobovy-agent in #1357
- actionAngleStaeckel: chi-anomaly quadrature at the turning points in C by @jobovy-agent in #1404
- actionAngleStaeckel C: frequencies and angles near turning points by @jobovy-agent in #1424
- actionAngleStaeckel C: NaN and near-axis accuracy in the chi quadratures by @jobovy-agent in #1431
- actionAngleAdiabatic: regularize the action integrands' sqrt endpoints (fixes #1354) by @jobovy-agent in #1356
- actionAngleSpherical: circular and near-circular orbits by @jobovy-agent in #1527
- actionAngleSpherical: decide boundedness at infinity, not at a fixed radius by @jobovy-agent in #1553
- OblateStaeckelWrapperPotential: choose the reference curve from delta by @jobovy-agent in #1379
- potentials: machine-precision small r (NFW, Burkert, TwoPower), finite edge limits, Staeckel midplane vmin by @jobovy-agent in #1585
- TwoPowerSphericalPotential: forces, second derivatives and mass from the incomplete beta (accurate near beta = 3, finite at large r) by @jobovy-agent in #1596
- eddingtondf: f(E) accurate near Emin (NFW was NaN/negative there) by @jobovy-agent in #1586
- Dynamical friction: sigma_r on a log-r grid (was 20-60% off at small r); Python evaluates C's natural spline by @jobovy-agent in #1633
- AnyAxisymmetricRazorThinDisk: fix the small-|z| force/2nd-derivative band (exact-complement K + a=R peak resolution) by @jobovy-agent in #1245
- AnyAxisymmetricRazorThinDisk: evaluate the second derivatives as finite-part integrals by @jobovy-agent in #1296
- AnyAxisym: Phi misses the midplane cusp for |z| << R (and the test that hid it) by @jobovy-agent in #1333
- RazorThinExponentialDisk: substitute out the k=R endpoint singularity by @jobovy-agent in #1295
- EinastoPotential: use regularized P so n=6 force stays finite by @shaneraphel with Cursor in #1501
- Two wrong tolerances: surfdens quadrature (epsabs) and the streamgapdf sample distance by @jobovy-agent in #1293
- jeans: don't let scipy.quad's default epsabs truncate sigma_r's small outer tail by @jobovy-agent in #1439
- AdiabaticContractionWrapperPotential: Phi0 from the exact integral of its force by @jobovy-agent in #1709
- NFW, ExpTruncNFW: small-r series in the jax radial force that constantbetadf differentiates by @jobovy-agent in #1760
- constantbetadf: jax derivatives in float64 without turning on jax's x64 for the user by @jobovy-agent in #1762
Bug fixes and minor tweaks
- Fix spherical DF sampling velocity bias by @jobovy-agent in #1568
- sphericaldf: evaluate a multi-orbit Orbit with each orbit's own L by @jobovy-agent in #1734
- interpRZPotential: keep grids and off-grid fallbacks in internal units by @jobovy-agent in #1274
- AnySphericalPotential: integrate element-wise for array r by @jobovy-agent in #1323
- Select per-orbit physical parameters when slicing Orbit arrays by @antonsoo in #1739
- Preserve Orbit array shapes in projections and physical output by @antonsoo in #1740
- C orbit integration: offsets in size_t (segfault beyond 2^31-1 output elements) by @jobovy-agent in #1741
- Fix AdiabaticContractionWrapperPotential ignoring amp; quasiisothermaldf: _precomputerg=False, unbound points in arrays, out-of-grid Lz by @jobovy-agent in #1671
- quasiisothermaldf: rg grid looked up at |Lz| and rl solved for arrays at once (fixes #1671's slowdown) by @jobovy-agent in #1703
- sphericaldf: honour a per-call ro=/vo= when parsing Quantity inputs by @jobovy with Claude in #1349
- Orbit: evaluate at a 0-d array time (fixes method='dop853' in a MovingObjectPotential) by @jobovy-agent in #1738
- Orbit: setting up from sky coordinates with uvw=True no longer modifies the input by @jobovy-agent in #1611
- Set alpha/beta for NFWPotential rmax/conc init paths by @steps-re in #1108
- Check interpSphericalPotential range independently of FDM friction by @steps-re in #1107
- actionAngleAdiabatic: fix multi-point _justjr raising TypeError by @jobovy-agent in #1398
- actionAngleVerticalInverse: select tori by their actual action with a polynomial point transformation by @jobovy-agent in #1510
- actionAngleVerticalInverse: compute the polynomial point transformation's action offset in closed form and apply it by @jobovy-agent in #1516
- actionAngle: stop masking an inner AttributeError as "method not implemented" by @jobovy-agent in #1355
- actionAngle_c: parse one potentialArg copy per OpenMP thread by @jobovy-agent in #1475
- integrate_dxdv: transform the base state in C, restoring bit-identity with plain integrate by @jobovy-agent in #1400
- Programatically define galpy animation <style> to avoid duplication and HTML error by @jobovy in #1588
Dependencies / maintenance
- Bump to 1.12.1.dev0 and start v1.12.1 HISTORY by @jobovy with Claude in #1042
- Fix sdist install glob; build/test sdist on every PR (upload still release-only) by @jobovy with Claude in #1044
- [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci[bot] in #1078
- [auto] Update documentation notebook outputs (2026-07-16) by @github-actions[bot] in #1135
- Bump actions/setup-python from 6 to 7 in the actions group by @dependabot[bot] in #1207
- Bump actions/stale from 10 to 11 in the actions group by @dependabot[bot] in #1240
- [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci[bot] in #1244
- tests: drop the expired numpy-version tolerance branch in test_SpiralArmsPotential by @jobovy-agent in #1253
- Bump pypa/cibuildwheel from 4.1 to 4.2 in the actions group by @dependabot[bot] in #1287
- Improve Claude diff analysis debugging and error messaging by @jobovy with Claude in #1326
- [auto] Update documentation notebook outputs (2026-08-16) by @github-actions[bot] in #1329
- Move the grid-quantity parser to the actionAngleInverse base module by @jobovy-agent in #1350
- CI: fetch GSL from the nearest GNU mirror rather than ftp.gnu.org by @jobovy in #1409
- tests: cut the time-dependent SCF coefficient tests from ~76s to ~11s by @jobovy-agent in #1418
- [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci[bot] in #1456
- test_orbit: one test case per potential for the four all-potentials walks by @jobovy-agent in #1467
- Delete copilot setup by @jobovy in #1472
- Avoid duplicate builds/tests on PRs by @jobovy in #1470
- [auto] Update documentation notebook outputs (2026-09-15) by @github-actions[bot] in #1502
- all-clear: ignore superseded check attempts by @jobovy with Codex in #1589
- [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci[bot] in #1632
- Pyodide build: fetch GSL from independent mirrors, SHA-256 verified (GNU hosts down) by @jobovy-agent in #1638
- Bump cibuildwheel to v4.3 and drop CIBW_ENABLE: cpython-prerelease by @jobovy with Claude in #1696
- test_integrate_c_more_than_intmax_elements: run the subprocess in the tested galpy's directory by @jobovy-agent in #1746
- CI on ubuntu-26.04 (GCC 15): declare gcov hooks, omp.h outside extern C, central differences in the planar STM test by @jobovy-agent in #1754
- Doc notebooks: nbody_snapshots' C potential built in kpc; full Gaia DR3 sample in staeckel by @jobovy-agent in #1782
- [auto] Update documentation notebook outputs (2026-11-02) by @github-actions[bot] in #1783
- Release 1.12.1 by @jobovy-agent in #1784
New Contributors
- @steps-re made their first contribution in #1108
- @shaneraphel made their first contribution in #1501
- @antonsoo made their first contribution in #1739
Full Changelog: v1.12.0...v1.12.1