First pass at EFT-based spherical geometry accuracy (port from AccuSphGeom)#1513
First pass at EFT-based spherical geometry accuracy (port from AccuSphGeom)#1513rajeeja wants to merge 9 commits into
Conversation
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
There was a problem hiding this comment.
We have an existed implementation for these functions in
uxarray/uxarray/utils/computing.py
Line 189 in 735a1dd
And I would suggest use other name instead of eft here, the term "EFT" specifically refers to "Error-Free" floating point operations, but the AccuCross and diff_of_productthemself is not completely Error-Free, is just more accurate than normal floating point cross (doubling the precision)
There was a problem hiding this comment.
Done: merged this into uxarray/utils/computing.py and renamed the docs/API section to compensated arithmetic. I only call two_sum/two_prod EFT now; the cross-product helpers are described as compensated algorithms.
|
|
||
|
|
||
| @njit(cache=True) | ||
| def _generate_lat_lon_bounds_local(face_vertices, z_min, z_max, snap_tol_deg): |
There was a problem hiding this comment.
For a better vectorization, consider keeping the current design in https://github.com/hongyuchen1030/AccuSphGeom/blob/56cbd6e30270ec5845a853ec72ba5b19c9128017/include/accusphgeom/algorithms/lat_lon_bounds.hpp#L107:
It uses lots of masking and avoid branching for computation steps.
There was a problem hiding this comment.
Done: rewrote this path closer to the AccuSphGeom structure. The local bounds computation now uses mask-selection for extrema/snap decisions instead of branching through separate cases.
|
|
||
|
|
||
| @njit(cache=True) | ||
| def _generate_lat_lon_bounds_pole(face_vertices, label, z_min, z_max, snap_tol_deg): |
There was a problem hiding this comment.
Same here, consider keeping the structure here: https://github.com/hongyuchen1030/AccuSphGeom/blob/56cbd6e30270ec5845a853ec72ba5b19c9128017/include/accusphgeom/algorithms/lat_lon_bounds.hpp#L159
use masks instead of branching as much as possible for the vectorization purpose
There was a problem hiding this comment.
Done: same cleanup here. The polar bounds path now keeps the AccuSphGeom-style local/polar structure and uses mask-selection for the snap logic.
|
|
||
|
|
||
| @njit(cache=True) | ||
| def _face_location_info(face_vertices, polar_cap_z): |
There was a problem hiding this comment.
The branching here will probably slow down the vectorization, consider keeping the structure here:
https://github.com/hongyuchen1030/AccuSphGeom/blob/56cbd6e30270ec5845a853ec72ba5b19c9128017/include/accusphgeom/algorithms/lat_lon_bounds.hpp#L49
There was a problem hiding this comment.
Done: _face_location_info now follows the AccuSphGeom face-location flow. The per-edge extrema use mask-selection; only the final face classification branches remain.
| @njit(cache=True) | ||
| def _normalize_pair(x_hi, y_hi, z_hi, x_lo, y_lo, z_lo): | ||
| """Normalize an (hi, lo) compensated vector, returning the unit vector and magnitude.""" | ||
| x = x_hi + x_lo |
There was a problem hiding this comment.
This _normalize_pair is not utilizing the EFT and it will have the same precision as the direct floating point precision. And the normalization is one of the big killer for the precision.
There was a problem hiding this comment.
Also if the input are normalized, then both the gca_gca_intersection and the gca_constLat_intersection will return the normalized results already
There was a problem hiding this comment.
And just in case you want an "accurately calculated norm", it is const auto sum = numeric::sum_of_squares_c<T, 3>(v.hi, v.lo); const auto norm = numeric::acc_sqrt_re(sum.hi, sum.lo);
There was a problem hiding this comment.
Done: removed _normalize_pair. The intersection code now keeps compensated normals/intersection vectors, and the baseline near-tangent cases pass.
| @@ -301,139 +314,198 @@ def _gca_gca_intersection_cartesian(gca_a_xyz, gca_b_xyz): | |||
|
|
|||
| @njit(cache=True) | |||
There was a problem hiding this comment.
consider keeping the entire structure from here : https://github.com/hongyuchen1030/AccuSphGeom/blob/e4e13215dd4771b7ef01b7edf81eaf58dd6e6995/include/accusphgeom/constructions/gca_gca_intersection.hpp#L31
These EFT-fused operations are extremely sensitive for each operations and order. The accuracy can only be guaranteed iff following the exact algorithm
There was a problem hiding this comment.
Done: rewrote GCA-GCA around the AccuSphGeom structure: accucross normals, accucross_pair for the intersection direction, then candidate filtering with on_minor_arc.
|
|
||
|
|
||
| @njit(cache=True) | ||
| def gca_const_lat_intersection(gca_cart, const_z): |
There was a problem hiding this comment.
The algorithm is almost very stable around floating point precision. Within the current Uxarray Error tolerance, you almost don't have to use any other safety pre-check other than if the input are valid (The relative error bound is 3*\sqrt{1-constz^2}*machine_epsilon as long as it's constZ is at least 10^15 from the equator) . And probably the only check needed Then only check you can make is if the const latitude is at the equator . Again, make sure to follow the entire algorithm structure from below. Such precision is only guaranteed
iff it follows the exact algorithm here
https://github.com/hongyuchen1030/AccuSphGeom/blob/e4e13215dd4771b7ef01b7edf81eaf58dd6e6995/include/accusphgeom/constructions/gca_constlat_intersection.hpp#L33
There was a problem hiding this comment.
But you can use the same check as in https://github.com/hongyuchen1030/AccuSphGeom/blob/e4e13215dd4771b7ef01b7edf81eaf58dd6e6995/include/accusphgeom/constructions/gca_gca_intersection.hpp#L51
to remove any invalid points
There was a problem hiding this comment.
Done: rewrote const-lat around the AccuSphGeom accux_constlat flow and filter invalid candidates after computing them.
| nx = nx_hi + nx_lo | ||
| ny = ny_hi + ny_lo | ||
| nz = nz_hi + nz_lo | ||
| denom = nx * nx + ny * ny |
There was a problem hiding this comment.
Denome should be calculated from
const T denom = s2.hi + s2.lo;
where
const auto s2 = numeric::sum_of_squares_c<T, 2>( {normal.hi[0], normal.hi[1]}, {normal.lo[0], normal.lo[1]});
There was a problem hiding this comment.
Done: denom now comes from _sum_sq_c2(...) as s2_hi + s2_lo.
| @@ -301,139 +314,198 @@ def _gca_gca_intersection_cartesian(gca_a_xyz, gca_b_xyz): | |||
|
|
|||
| @njit(cache=True) | |||
| def gca_gca_intersection(gca_a_xyz, gca_b_xyz): | |||
There was a problem hiding this comment.
Consider keeping the entire structure from here https://github.com/hongyuchen1030/AccuSphGeom/blob/e4e13215dd4771b7ef01b7edf81eaf58dd6e6995/include/accusphgeom/constructions/gca_gca_intersection.hpp#L31.
These algorithms are extremely sensitive to the operations and order, the accuracy is only guaranteed iff we follow the exact same algorithm described
There was a problem hiding this comment.
Done: same GCA-GCA rewrite as above, following the AccuSphGeom operation order much more closely. Added baseline tests for the near-tangent cases too.
| x2_at_const_z = np.isclose( | ||
| x2[2], const_z, rtol=ERROR_TOLERANCE, atol=ERROR_TOLERANCE | ||
| ) | ||
| # 1. Endpoint coincidence with the latitude line. |
There was a problem hiding this comment.
This case is still valid and calculable with the new algorithm. And we can improve the vectorization by only use mask-selection after the intersection is calculated to snap the intersection point with the endpoints
There was a problem hiding this comment.
Done: removed the endpoint early return. The code computes candidates first, then snaps near-endpoint results afterward.
| z_max = extreme_gca_z(gca_cart, extreme_type="max") | ||
|
|
||
| # Check if the constant latitude is within the GCA range | ||
| if not in_between(z_min, const_z, z_max): |
There was a problem hiding this comment.
I am not sure if this early exist will counter-effect the branching it brings to the vectorization
There was a problem hiding this comment.
Done: removed the endpoint pre-exits. I kept only invalid denom / negative-discriminant exits.
| elif p2_intersects_gca: | ||
| res[0] = p2 | ||
| # 4. Solve for the two candidate points on the latitude circle. | ||
| r2 = 1.0 - const_z * const_z |
There was a problem hiding this comment.
This is not the new EFT-fused algorithmn
There was a problem hiding this comment.
Done: replaced this with the compensated s2/s3, two_prod, cdp4, and acc_sqrt_re flow from AccuSphGeom.
|
@hongyuchen1030 Thanks for the review. The point-in-polygon predicate and
|
…rite intersections, add 241 baseline testsgit status! - most came from accusphere
… into rajeeja/accusphere
Closes #1509
Ports EFT primitives from AccuSphGeom (Chen 2026) into
uxarray/grid/_eft.pyand replaces naive cross-product arithmetic inarcs.py,intersections.py,bounds.py, andpoint_in_face.pywith EFT-backedorient3d_on_sphereandon_minor_arc. Adds a user guide notebook covering the problem, API, and a live point-in-polygon demo.