Releases#
PYTRUNC CHANGELOG#
v2.1.0#
Release date: 30-09-2026
New features#
Add the continuous plateau to
gt_phase_approx: withtrunc_frac=Noneand an imposed angleth_f, the phase matrix is cut flat at its value atth_fand the truncation fraction follows from that continuity (returned inf; the new output variabletrunc_fracechoes the parameter, None in that case). Imposing bothtrunc_fracandth_fsets the plateau by the normalization instead, in general not continuous: 2.5 times the truncated phase matrix at 5 degrees for Fournier-Forand (n = 1.10, mu = 3.5) andtrunc_frac=0.3, where the continuous plateau takes f = 0.467. The docstring Notes describe the three ways the angle, the fraction and the plateau set each other
Other changes#
Fix the truncation angle search of
gt_phase_approx, which could return a truncated phase matrix with a negative plateau: for a peak as sharp as Fournier-Forand’s, the first moment matches best at angles within which the peak holds less than the fractiontrunc_fracof the scattering, where the plateau is negative (-445 at 1.1 degree for n = 1.10, mu = 3.5,trunc_frac=0.3and the trapezoid rule). The search now skips them, and raises a ValueError when no angle belowth_tolleaves a non-negative plateau. An imposed angle (th_f) is unchanged. The searched references of the two-term Henyey-Greenstein test case, whose plateaus were negative, are regenerated
v2.0.0#
Release date: 06-08-2026
Breaking changes#
Rename the
constantmodule toconstants, for consistency with the geoclide package. The imports becomefrom pytrunc.constants import VERSIONandfrom pytrunc.constants import DIR_ROOTFix
delta_m_phase_approxignoring itsmethodparameter: the moments were always computed with the calc_moments default ‘lobatto’, whatever the caller asked. The moments now honormethod, so the results change slightly, including with the default ‘trapezoid’ (the docstring example f value moves from 0.27248273 to 0.27250572)
Other changes#
Fix the
bessel_j1large-x branch: it indexed the wrong mask and raised a ValueError for any input containing x > 35. The branch now uses Hankel’s asymptotic expansion (Abramowitz & Stegun eq. 9.4.6), accurate to ~1e-10; the x <= 35 series is unchangedReplace the bare print of
calc_momentson an out-of-range theta with awarnings.warn(UserWarning)Performance pass, bit-identical results (verified bitwise against a 122-array reference of gt_phase_approx, delta_m_phase_approx, calc_moments and quadrature_lobatto outputs):
Advance the three-term Legendre recurrence incrementally across the moment loops of
calc_momentsanddelta_m_phase_approxinstead of restarting it from degree 0 at each degree (O(m_max²) → O(m_max) recurrence work): calc_moments is ~30x faster at m_max=256 and delta_m_phase_approx ~18x at m_max=128Hoist the loop invariants out of the
gt_phase_approxtruncation angle search (phase×sinθ product, min/max reductions on monotone slices, argsorts replaced by reversal views, a reused scratch buffer for the candidate phase, sortedness detection so that integrate_lobatto skips its per-call argsort): the search is ~1.2-1.4x faster depending on the methodFuse the first and second derivative Legendre recurrences of the Lobatto node solve into one loop (
_legendre_dp_d2p)
Remove dead code and redundant work: an always-true condition in the gt_phase_approx winner test, a duplicate allocation, defensive copies that protected nothing (
.copy()on freshly allocated arrays and on multiplication results), a duplicated phase-size check, scalar conditions expressed as np.where, and a wrong error message in bessel_jn (“x must be >= 1” for a x < 0 check)Deduplicate repeated blocks: the theta_unit validation (4 copies →
_theta_to_rad), the Dirac normalization of the two truncation methods (→_dirac_delta_part, with its wrong “sin(pi) = 0” comment corrected to sin(0) = 0), the Henyey-Greenstein kernel of the two-term variant (→_hg_from_mu, converting theta and sorting mu once),calc_tthg_momentsreusingcalc_hg_moments, and the three copies of the truncation candidate evaluation in gt_phase_approx (forced th_f mode, search seed and search loop → one local closure). An unknownmethodnow raises a ValueError instead of a KeyErrorUse lowercase names for the local mathematical variables, following the PEP 8 naming conventions:
P0,P1,Pn,Pnm1,Pnp1and their derivative variants in the utils module,PfandPf_tmpin the truncation module, andF_HG1andF_HG2in the phase module. The mathematical notation of the docstrings is unchanged, and the ruff configuration now selects the pep8-naming rulesReflow the docstrings and comments of every module, filling the 72-column budget instead of only enforcing it, add docstrings to the private integrator wrappers of the truncation module, and shorten the Ocean Optics Web Book references so that they fit in the limit
Commit the pixi lock file, previously ignored: the tests and docs workflows install the environment from it, and failed at the pixi setup step without it
Add the pytrunc logo, in a light and a dark variant, at the top of the README and in the documentation sidebar, both following the light/dark theme
Add a documentation landing page (docs/introduction.md, included in index.rst) presenting the features, a quickstart and the contents of the documentation
Improve the README: features, installation, quickstart, examples, documentation and license sections, and badges for the tests, the documentation, the license, pixi and ruff
Add a GitHub workflow running ruff, and lint the documentation configuration and notebook sources accordingly
Add the
fournier_forandphase function to the phase module, following the same conventions ashenyey_greenstein(theta_unit and normalize parameters), with a direct unit test suite (tests/test_phase.py)Docstring convention pass aligned with the geoclide package: 72-column docstring limit enforced by ruff (max-doc-length), numpydoc style with Examples sections on all public functions, unified References format, and module descriptions in every module including the test modules
Modernize the license metadata: SPDX expression (license = “Apache-2.0”) with license-files, rename LICENSE.TXT to LICENSE.txt, remove the deprecated license classifier and require setuptools >= 77
v1.1.0#
Release date: 31-07-2026
Major speed-up of the
gt_phase_approxfunction with method=’lobatto’The Lobatto quadrature abscissas and weights are now cached, avoiding the expensive node computation at each call
Faster interpolation in
integrate_lobattoThe parameter lobatto_optimization is now True by default
Fix scipy >= 1.14 compatibility (the x argument of the scipy simpson function became keyword-only)
Fix an UnboundLocalError in
gt_phase_approxwhen the truncation angle search does not improve on its starting pointAdd a pytest regression test suite (tests directory)
Add a GitHub workflow for automatic publishing to PyPI on version tags
Code modernization: type hints in all modules, ruff lint + format (line length 79), pyright clean, docstrings and comments reflowed to 72 columns
v1.0.1#
Release date: 06-02-2026
Modify python minimum version to 3.11
VERSION constant automatically read from pyproject toml file
Add missing information to pyproject toml file
v1.0.0#
Release date: 06-02-2026
First release