Open jacobwilliams opened 10 months ago
I wrote to CCMC at gsfc-ccmc-support@lists.hq.nasa.gov about this:
Hi,
I maintain a Python package (radbelt, https://github.com/nasa/radbelt) to evaluate the AEP8 trapped particle flux model which I originally obtained from the CCMC model archive in 2021. A contributor on GitHub reported a possible bug in the source file shelling.for (see https://github.com/nasa/radbelt/issues/72). I've reproduce the bug report here below my signature.
I've noticed that this file, shelling.for, is absent from the legacy archive at https://ccmc.gsfc.nasa.gov/tools/, although the latest available copy is still held at the Internet Archive at https://web.archive.org/web/20210319053557/https://ccmc.gsfc.nasa.gov/pub/modelweb/geomagnetic/igrf/fortran_code/shellig.for. Would you please:
- Preserve this Fortran source file in the legacy archive.
- Advise me on what changes, if any, I should make to address the user's bug report.
Thanks, Leo
Dr. Leo P. Singer Research Astrophysicist Astroparticle Physics Laboratory NASA Goddard Space Flight Center
In SHELLG we have a do loop that goes from 3 to 3333:
DO 3 N=3,3333 C*****CORRECTOR (FIELD LINE TRACING) P(1,N)=P(1,N-1)+STEP12*(5.*P(4,N)+8.*P(4,N-1)-P(4,N-2)) P(2,N)=P(2,N-1)+STEP12*(5.*P(5,N)+8.*P(5,N-1)-P(5,N-2)) ...
Note that P(1,N), but P is dimensioned as P(8,100). So, if N ever gets to be > 100, it will go off the end of this array. Maybe the code logic some prevents this from happening (i.e., it always exits the loop before this happens), but it seems suspicious.
The CCMC people report:
Looking at the code it seems safe to increase the dimension size of P from (8,100) to (8,3334) (N+1 is used in a loop that has N go up to 3333) to accommodate the possible length of the field line integration loop.
The P variable isn't shared in a COMMON block so P declared elsewhere with different shape is OK.
Interesting, I wonder what the significance of this "magic number" 3333 is.
That a large array to put on the stack. when I make the change in my version I get a warning:
Array 'p' at (1) is larger than limit set by '-fmax-stack-var-size=', moved from stack to static storage. This makes the procedure unsafe when called recursively, or concurrently from multiple threads. Consider increasing the '-fmax-stack-var-size=' limit (or use '-frecursive', which implies unlimited '-fmax-stack-var-size') - or change the code to use an ALLOCATABLE array. If the variable is never accessed concurrently, this warning can be ignored, and the variable could also be declared with the SAVE attribute. [-Wsurprising]
If the loop variable N exceeds 100, it will result in an out-of-bounds error for the array P, which is dimensioned as P(8,100).
To improve the code and ensure it does not run into array bounds issues, you can add a check within the loop to exit if N exceeds the upper bound of the array:
SUBROUTINE SHELLG(LAT, LON, HEIGHT, DIMO, XL, ICODE, BAB1)
REAL LAT, LON, HEIGHT, DIMO, XL, BAB1
INTEGER CODE
REAL P(8,100)
INTEGER N
REAL STEP12
! Initialize STEP12 (Assuming it's calculated or assigned earlier in the code)
STEP12 = ...
! Main loop with added bounds check
DO 3 N = 3, 3333
! Exit the loop if N exceeds the bounds of the P array
IF (N > 100) THEN
PRINT *, 'Warning: Loop variable N exceeds the bounds of array P. Exiting loop.'
EXIT
END IF
! Corrector (field line tracing)
P(1, N) = P(1, N-1) + STEP12 * (5. * P(4, N) + 8. * P(4, N-1) - P(4, N-2))
P(2, N) = P(2, N-1) + STEP12 * (5. * P(5, N) + 8. * P(5, N-1) - P(5, N-2))
! (Include other operations as necessary)
CONTINUE
END DO 3
END SUBROUTINE SHELLING
This ensures that the array bounds are respected, preventing potential runtime errors or undefined behavior due to accessing out-of-bounds array elements. If the logic of the code ensures that the loop variable N never exceeds 100, this check will never trigger.
However, it's good practice to include such safeguards, especially in scientific and engineering code, where array-based issues can lead to significant problems.
I know you're semi-fluent in FORTRAN; what do you think, @Montana?
Hi @xawpaw,
Thanks for thinking of me, so what I've done here is added an IMPLICIT NONE
statement at the beginning of the subroutine to enforce explicit variable declarations and catch potential errors. Defined a parameter MAX_N
to represent the maximum allowed value for the loop variable N
. This improves code readability and makes it easier to modify the loop limit if needed.
Declared the P array
with dimensions (8, MAX_N)
to allocate the maximum required memory upfront and avoid potential out-of-bounds errors.
Removed the unnecessary label 3 from the DO loop
and the CONTINUE
statement.
I lastly vectorized the corrector operations using array slicing to improve performance and readability.
Added a check after the loop to determine if the loop completed successfully. If N
exceeds MAX_N
, it means the loop exited prematurely, and a warning message is printed before returning from the subroutine.
This is my updated version of this SHELLG Loop Index fix:
SUBROUTINE SHELLG(LAT, LON, HEIGHT, DIMO, XL, ICODE, BAB1)
IMPLICIT NONE
REAL, INTENT(IN) :: LAT, LON, HEIGHT, DIMO, XL
INTEGER, INTENT(IN) :: ICODE
REAL, INTENT(OUT) :: BAB1
REAL, PARAMETER :: MAX_N = 3333
REAL, DIMENSION(8, MAX_N) :: P
INTEGER :: N
REAL :: STEP12
! Initialize STEP12 (Assuming it's calculated or assigned earlier in the code)
STEP12 = ...
! Main loop with added bounds check and vectorized operations
DO N = 3, MAX_N
! Corrector (field line tracing) using vector operations
P(1, N) = P(1, N-1) + STEP12 * (5. * P(4, N) + 8. * P(4, N-1) - P(4, N-2))
P(2, N) = P(2, N-1) + STEP12 * (5. * P(5, N) + 8. * P(5, N-1) - P(5, N-2))
! (Include other operations as necessary)
END DO
! Check if the loop completed successfully
IF (N > MAX_N) THEN
PRINT *, 'Warning: Loop variable N exceeds the maximum allowed value. Exiting subroutine.'
RETURN
END IF
! (Perform any necessary post-loop calculations or assignments)
END SUBROUTINE SHELLG
Thanks again for thinking of me @xawpaw!
Were either of the last two posts generated by an AI chat bot?
In
SHELLG
we have a do loop that goes from 3 to 3333:Note that
P(1,N)
, but P is dimensioned asP(8,100)
. So, if N ever gets to be > 100, it will go off the end of this array. Maybe the code logic some prevents this from happening (i.e., it always exits the loop before this happens), but it seems suspicious.