Skip to content

Conversation

@krystophny
Copy link
Member

@krystophny krystophny commented Jan 15, 2026

Summary

Fix inconsistency between xend_cart and wall_hit_cart for STL wall hits.

Root cause: For STL wall intersections detected by CGAL, xend_cart was computed from zend via forward chartmap transformation. When the earlier from_cart() inverse transformation failed (in ill-conditioned chartmap regions), zend contained incorrect reference coordinates, causing xend_cart to be displaced from the true wall intersection.

Fix (2 commits):

  1. simple_main.f90: Add linear interpolation fallback for zend when from_cart() fails
  2. netcdf_results_output.f90: Use wall_hit_cart directly for xend_cart on STL hits (the clean fix)

The second fix is the definitive solution - xend_cart now equals wall_hit_cart exactly for all STL wall hits, regardless of whether from_cart() succeeds. The first fix improves zend for angle-space analysis.

Also: Update CGAL 5.6.1 -> 6.0.1 for GCC compatibility, and set xend_cart to zero (not xstart_cart) for untraced particles.

Related

Test plan

  • CI build passes
  • Manual verification with STL wall runs

When CGAL detects a wall intersection, wall_hit_cart correctly stores
the intersection point in Cartesian coordinates. The code then calls
from_cart() to convert this back to reference coordinates for zend.

However, when from_cart() Newton iteration fails to converge (ierr != 0),
the reference coordinates z(1:3) were not updated, causing zend and
xend_cart to record the particle position PAST the wall rather than AT
the wall intersection.

This fix adds a linear interpolation fallback: when from_cart() fails,
interpolate reference coordinates along the orbit segment using the
fractional distance to the Cartesian hit point.

Also update CGAL from 5.6.1 to 6.0.1 to fix GCC compilation error with
Halfedge_around_source_iterator::base().

Test results show significant improvement:
- Before: 32.1% of wall hits had >100cm discrepancy
- After:  3.6% of wall hits have >100cm discrepancy
@krystophny
Copy link
Member Author

Visual Evidence

CGAL Wall Hit Position Consistency

The histogram compares position differences between wall_hit_cart (true CGAL intersection) and xend_cart (computed from reference coordinates) before and after this fix.

Key improvements:

  • Excellent agreement (<1 cm): 65.2% -> 78.6%
  • Severe discrepancy (>500 cm): 22.3% -> 0%

Test configuration: SQUID stellarator, 1ms simulation, 1024 particles starting at s=0.6, using CGAL-embedded STL chartmap.

@qodo-code-review
Copy link

qodo-code-review bot commented Jan 15, 2026

ⓘ Your approaching your monthly quota for Qodo. Upgrade your plan

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Supply chain integrity

Description: The build fetches and builds a third-party dependency (CGAL) directly from a remote URL
via FetchContent_Declare(URL ...) without any pinned integrity verification (e.g.,
URL_HASH/checksum or signature verification), which creates a software supply-chain risk
if the downloaded archive is tampered with or replaced upstream.
CMakeLists.txt [209-223]

Referred Code
include(FetchContent)
FetchContent_Declare(
    cgal
    URL https://github.com/CGAL/cgal/releases/download/v6.0.1/CGAL-6.0.1.tar.xz
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_GetProperties(cgal)
if(NOT cgal_POPULATED)
    FetchContent_Populate(cgal)
endif()
# CGAL 6.x is header-only, just add include path
set(CGAL_INCLUDE_DIR "${cgal_SOURCE_DIR}/include")
message(STATUS "CGAL enabled via FetchContent: 6.0.1 (header-only)")
message(STATUS "CGAL include dir: ${CGAL_INCLUDE_DIR}")
add_compile_definitions(SIMPLE_ENABLE_CGAL)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Fallback edge case: The new interpolation fallback silently does nothing when segment_length is zero, leaving
z(1:3) potentially unupdated after from_cart() failure and risking the same inconsistency
the fix aims to address.

Referred Code
segment_length = sqrt(sum((x_cur_m - x_prev_m)**2))
if (segment_length > 0.0_dp) then
    hit_distance = sqrt(sum((x_hit_m - x_prev_m)**2))
    t_frac = hit_distance / segment_length
    u_ref_hit = u_ref_prev + t_frac * (u_ref_cur - u_ref_prev)
    call ref_to_integ(u_ref_hit, z(1:3))
end if

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@krystophny
Copy link
Member Author

Update: The visual evidence histogram is available locally at /tmp/cgal_fix_comparison.png showing:

Position Discrepancy    Before Fix    After Fix
-------------------------------------------
< 1 cm (excellent)       65.2%         78.6%
> 100 cm (large)         32.1%          3.6%
> 500 cm (severe)        22.3%            0%

The fix significantly improves consistency between the CGAL-reported wall intersection (wall_hit_cart) and the position computed from reference coordinates (xend_cart).

@qodo-code-review
Copy link

qodo-code-review bot commented Jan 15, 2026

ⓘ Your approaching your monthly quota for Qodo. Upgrade your plan

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Handle zero-length segment case

Add an else branch to handle the zero-length segment case by setting u_ref_hit
to u_ref_prev, resetting ierr_from_cart, and calling ref_to_integ to prevent an
uninitialized state.

src/simple_main.f90 [634-639]

 if (segment_length > 0.0_dp) then
     hit_distance = sqrt(sum((x_hit_m - x_prev_m)**2))
     t_frac = hit_distance / segment_length
     u_ref_hit = u_ref_prev + t_frac * (u_ref_cur - u_ref_prev)
+    ierr_from_cart = 0
+    call ref_to_integ(u_ref_hit, z(1:3))
+else
+    u_ref_hit = u_ref_prev
+    ierr_from_cart = 0
     call ref_to_integ(u_ref_hit, z(1:3))
 end if
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug in the new fallback logic where a zero-length segment would leave the particle state un-updated and the error flag set, leading to incorrect behavior. The proposed fix is comprehensive and correct.

Medium
General
Clamp interpolation fraction

Clamp the interpolation fraction t_frac to the [0, 1] range to guard against
numerical errors and prevent extrapolation.

src/simple_main.f90 [636]

-t_frac = hit_distance / segment_length
+t_frac = min(1.0_dp, max(0.0_dp, hit_distance / segment_length))
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This suggestion correctly identifies a potential numerical instability where hit_distance could be slightly larger than segment_length, leading to extrapolation. Clamping t_frac makes the interpolation more robust against floating-point inaccuracies.

Low
  • Update

For STL wall hits, xend_cart should equal wall_hit_cart (the true CGAL
intersection point). Previously, xend_cart was computed from zend via
forward chartmap transformation, which could produce incorrect positions
when the earlier from_cart inverse transformation failed.

This fix ensures xend_cart is always correct for STL hits by using
wall_hit_cart directly, avoiding the round-trip coordinate conversion.

Also set xend_cart to zero (not xstart_cart) for untraced particles.

Test results:
- PRE-FIX: 2/50 exact matches, max error 1595 cm
- POST-FIX: 50/50 exact matches, zero error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants