Skip to content

fix: align Lorentz/planar coordinate conventions across signatures#717

Draft
henryiii wants to merge 1 commit into
mainfrom
fix/lorentz-coordinate-consistency
Draft

fix: align Lorentz/planar coordinate conventions across signatures#717
henryiii wants to merge 1 commit into
mainfrom
fix/lorentz-coordinate-consistency

Conversation

@henryiii

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Summary

Several _compute functions returned different results for the same vector depending on which coordinate system it was stored in. This PR aligns them so all 12 Lorentz coordinate systems (and the planar/spatial helpers) agree, following the project convention of matching ROOT's TLorentzVector / Math::LorentzVector.

Each fix was reproduced before being changed; regression tests cover both the T- and Tau-coordinate representations.

Bugs fixed

  1. Et sign convention differed by signature. xy_z_t/xy_z_tau returned sqrt(Et2) (always ≥0) while the theta/eta/rhophi variants returned t·sinθ (sign-preserving in t). E.g. vector.obj(px=3, py=4, pz=0, E=-13).Et gave +13 but vector.obj(pt=5, phi=0, pz=0, E=-13).Et gave -13. Now xy_z_* use copysign(sqrt(Et2), t), matching ROOT's TLorentzVector::Et() (E<0 ? -sqrt(Et2) : sqrt(Et2)).

  2. Mt/Mt2 disagreed between T and Tau coordinates. The T-variants computed t²−z² unclamped (so Mt=nan for transverse-spacelike vectors), while the Tau-variants clamped with maximum(…, 0) (giving Mt=0). E.g. vector.obj(px=3, py=4, pz=10, E=2).Mt2 == -96, .Mt == nan, but the equivalent tau vector gave 0.0. Now both representations return the signed Mt2 = t²−z² (the Tau-variants reconstruct it as tau2 + rho², where tau2 is the signed copysign(tau², tau)), and Mt = copysign(sqrt(|Mt2|), Mt2), matching ROOT's Mt().

  3. Lorentz scale flipped tau's sign for negative factors. The Tau-variants returned tau * factor; since negative tau encodes spacelike, scaling a timelike vector by −2 produced a spacelike encoding, disagreeing with the T-coordinate version. Now tau * |factor|: scaling by λ multiplies t²−mag² by λ², so |tau| scales by |λ| and the causal character is invariant. (spatial/scale.py already takes |factor| for rho.)

  4. Planar unit of the zero vector was inconsistent. xy used nan_to_num(…, nan=0) (zero → (0,0)) but rhophi returned (1, phi) unconditionally. rhophi now mirrors spatial/unit.py: rho=0 → 0.

  5. spatial/eta.py inconsistent nan guard. xy_theta wrapped -log(tan(θ/2)) in nan_to_num(nan=0.0, …) but rhophi_theta returned the raw expression (giving nan for θ outside (0, π)). The same guard was added to rhophi_theta.

All changes stay as array-safe lib.* expressions, so the numba (register_jitable), awkward, and sympy backends keep working (copysign/maximum/nan_to_num have sympy shims). Two existing scale tau-coordinate test assertions and the sympy Mt _t assertions encoded the previous inconsistent behavior and were updated to the now-consistent values.

Convention decisions for maintainer review

These are physics-convention choices — please confirm they match the intended ROOT semantics:

  • Et is sign-preserving in energy (negative E → negative Et), per ROOT TLorentzVector::Et(). Applied uniformly to all 12 signatures.
  • Mt/Mt2 are signed (Mt2 = t²−z² unclamped; Mt = copysign(sqrt(|Mt2|), Mt2)), per ROOT Mt(), so transverse-spacelike vectors give a negative Mt rather than nan/0. The previous Tau-variant clamp to 0 is removed.
  • scale preserves causal character: |tau| scales by |factor|, sign unchanged. A side effect: for vectors stored in Tau coordinates, (v * negative).t now reports the (always-positive) reconstructed |t| with the correct timelike/spacelike magnitude instead of the previous sign-flipped value — the two updated assertions in tests/compute/test_scale.py reflect this.
  • Zero-vector unit maps to the zero vector in both xy and rhophi planar representations (matching the spatial backend).

Note: a Tau-coordinate vector does not store the sign of t, so (v*negative).t reconstructed from tau is always non-negative; this PR makes its magnitude consistent across coordinate systems but does not attempt to recover the lost sign (out of scope).

Tests

Added regression cases for: negative-energy Et across all signatures; transverse-spacelike Mt/Mt2 across both T- and Tau-coordinate vectors; negative scale factor on Tau-coordinates (timelike and spacelike); zero-vector planar unit in rhophi (object + numpy); and eta for θ outside (0, π) in both xy_theta and rhophi_theta.

uv run pytest tests/compute/ tests/backends/test_object.py tests/backends/test_numpy.py tests/backends/test_sympy.py tests/backends/test_awkward.py tests/backends/test_numba_object.py — all green.

Part of #711

🤖 Generated with Claude Code

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.18%. Comparing base (d9ef1db) to head (09245bb).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #717      +/-   ##
==========================================
+ Coverage   87.16%   87.18%   +0.01%     
==========================================
  Files          96       96              
  Lines       11199    11211      +12     
==========================================
+ Hits         9762     9774      +12     
  Misses       1437     1437              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Several compute functions produced different results for the same vector
depending on which coordinate system it was stored in. Align them with
ROOT's TLorentzVector conventions:

- Et: make xy_z_t/xy_z_tau sign-preserving in energy (copysign on t),
  matching the rhophi/theta/eta variants and ROOT's Et().
- Mt/Mt2: drop the clamp on the tau-variants and make Mt sign-preserving
  via copysign-sqrt, so transverse-spacelike vectors agree between the T
  and Tau representations (and match ROOT's Mt()).
- scale: scale tau by |factor| so a negative factor no longer flips a
  timelike vector into the spacelike (negative-tau) encoding.
- planar unit: rhophi now sends the zero vector to 0 (like the xy variant
  and spatial/unit.py) instead of returning (1, phi).
- spatial eta: add the same nan->0 guard to rhophi_theta that xy_theta
  already had for theta outside (0, pi).

Updates two scale tau-coordinate tests and the sympy Mt _t assertions
that encoded the previous (inconsistent) behavior, and adds regression
tests for each case.

Part of #711

Assisted-by: ClaudeCode:claude-opus-4.8
@henryiii henryiii force-pushed the fix/lorentz-coordinate-consistency branch from dc55130 to 09245bb Compare June 16, 2026 04:04
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.

1 participant