Repository navigation
Releases
v2.1.0
Release date: 30-09-2026
New features
- Add the continuous plateau to
gt_phase_approx: with
trunc_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_frac
echoes the parameter, None in that case). Imposing bothtrunc_frac
andth_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 below
th_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_ROOT -
Fix
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 unchanged -
Replace 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 ofcalc_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=128 - Hoist 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 method - Fuse the first and second derivative Legendre recurrences of the
Lobatto node solve into one loop (_legendre_dp_d2p)
- Advance the three-term Legendre recurrence incrementally across the
-
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 KeyError -
Use 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 rules -
Reflow 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
Version 1.1.0
-
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_lobatto - The parameter lobatto_optimization is now True by default
- The Lobatto quadrature abscissas and weights are now cached, avoiding
-
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 point -
Add 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
Version 1.0.1
- VERSION constant automatically read from pyproject toml file
- Python minimum version is now 3.11