What You’ll Learn Today
Fortran counts differently than some other programming languages, and this matters A LOT when working with OpenACC! It’s like the difference between floor numbers in American buildings (1st floor, 2nd floor) versus British buildings (ground floor, 1st floor).
The Big Difference: Starting from 1
Fortran Way (starts from 1):
Array positions: [1] [2] [3] [4] [5]
Array values: [10][20][30][40][50]
C/Python Way (starts from 0):
Array positions: [0] [1] [2] [3] [4]
Array values: [10][20][30][40][50]
Think of it like house numbers on a street – some streets start numbering from 1, others from 0!
Why This Matters for OpenACC
When you write parallel loops, you need to get the indexing right, or you’ll access the wrong memory locations:
! CORRECT Fortran way
do i = 1, n
array(i) = i * 2
end do
! WRONG (this would miss the first element!)
do i = 0, n-1
array(i+1) = i * 2 ! Confusing and error-prone
end do
Visual: Array Bounds in Parallel Regions
Fortran Array Declaration: real :: numbers(100)
Memory Layout:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ (1) │ (2) │ (3) │ (4) │ ... │ (98)│ (99)│(100)│
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
Valid indices: 1, 2, 3, ..., 98, 99, 100
Invalid: 0, 101, 102, ... (will cause errors!)
Custom Array Bounds
Fortran lets you define custom starting and ending points:
! Array from 0 to 10
real :: temperatures(0:10)
! Array from -5 to 5
real :: displacement(-5:5)
! Array from 1990 to 2024 (great for years!)
real :: yearly_data(1990:2024)
This is like numbering floors in a building: basement (-1), ground (0), first (1), etc.
Boundary Conditions in Parallel Loops
Always be careful with loop boundaries:
! Safe parallel loop
!$acc parallel loop
do i = 1, n
if (i <= n) then ! Extra safety check
array(i) = array(i) * 2
end if
end do
!$acc end parallel loop
Index Mapping Between Different Systems
When working with data from other sources:
Data from C program (0-based): [0][1][2][3][4]
Transfer to Fortran (1-based): [1][2][3][4][5]
Mapping: fortran_index = c_index + 1
Common Indexing Mistakes to Avoid
❌ Off-by-one errors:
do i = 0, n ! Wrong! Should be 1, n
❌ Accessing beyond bounds:
do i = 1, n+1 ! Wrong! Goes beyond array size
❌ Forgetting custom bounds:
real :: data(-10:10)
do i = 1, 10 ! Wrong! Should be -10, 10
Visual: Multi-dimensional Array Indexing
2D Array: matrix(3,4)
Column: 1 2 3 4
Row 1: (1,1)(1,2)(1,3)(1,4)
Row 2: (2,1)(2,2)(2,3)(2,4)
Row 3: (3,1)(3,2)(3,3)(3,4)
Valid ranges: i = 1 to 3, j = 1 to 4
Key Points to Remember
- Fortran arrays start from index 1 (not 0)
- Always use 1 to n in your parallel loops (unless custom bounds)
- Check array bounds carefully – going outside causes crashes
- Custom bounds are allowed – use them when they make sense
- Be extra careful when converting from other languages
Example Code
Let us consider the following OpenACC code –
program proper_indexing
! This program demonstrates correct Fortran array indexing with OpenACC
implicit none
! Standard 1-based arrays
integer, parameter :: n = 20
real :: data(n) ! Indices: 1, 2, 3, ..., 20
real :: results(n) ! Indices: 1, 2, 3, ..., 20
integer :: i
! Initialize with proper 1-based indexing
write(*,*) 'Initializing arrays with 1-based indexing...'
do i = 1, n
data(i) = real(i) * 3.14159 ! Each element = i * π
end do
! Parallel computation with correct bounds
write(*,*) 'Running parallel loop with proper indexing...'
!$acc parallel loop
do i = 1, n ! CORRECT: starts from 1, ends at n
results(i) = data(i) * data(i) ! Square each element
end do
!$acc end parallel loop
! Display results showing proper indexing
write(*,*) 'Results (showing 1-based indexing):'
do i = 1, 10 ! Show first 10 elements
write(*,'(A,I0,A,F8.3,A,F10.3)') 'Element [', i, ']: ', &
data(i), ' → ', results(i)
end do
! Verify bounds are correct
write(*,*) ''
write(*,'(A,I0)') 'Array size: ', n
write(*,'(A,I0,A,I0)') 'Valid indices: ', 1, ' to ', n
write(*,'(A,F8.3)') 'First element data(1) = ', data(1)
write(*,'(A,F8.3)') 'Last element data(' // trim(str(n)) // ') = ', data(n)
contains
function str(num) result(string)
integer, intent(in) :: num
character(len=10) :: string
write(string, '(I0)') num
end function str
end program proper_indexing
To compile this code –
nvfortran -acc -o proper_indexing proper_indexing.f90
To execute this code –
./proper_indexing
Sample output –
Initializing arrays with 1-based indexing...
Running parallel loop with proper indexing...
Results (showing 1-based indexing):
Element [1]: 3.142 → 9.870
Element [2]: 6.283 → 39.478
Element [3]: 9.425 → 88.826
Element [4]: 12.566 → 157.913
Element [5]: 15.708 → 246.740
Element [6]: 18.850 → 355.305
Element [7]: 21.991 → 483.610
Element [8]: 25.133 → 631.654
Element [9]: 28.274 → 799.437
Element [10]: 31.416 → 986.959
Array size: 20
Valid indices: 1 to 20
First element data(1) = 3.142
Last element data(20) = 62.832
Let us consider the another OpenACC code –
program custom_bounds
! This program shows how to use custom array bounds with OpenACC
implicit none
! Arrays with custom bounds
real :: temperatures(-10:10) ! From -10°C to +10°C
real :: yearly_sales(2020:2024) ! Years 2020 to 2024
real :: height_data(0:100) ! Heights from 0cm to 100cm
integer :: i, year
write(*,*) 'Working with custom array bounds in OpenACC!'
write(*,*) ''
! Example 1: Temperature data with negative indices
write(*,*) '1. Temperature array (indices -10 to +10):'
!$acc parallel loop
do i = -10, 10
temperatures(i) = real(i) * 1.5 ! Temperature formula
end do
!$acc end parallel loop
! Show some temperature results
do i = -5, 5, 2
write(*,'(A,I0,A,F6.2,A)') 'Temperature at index ', i, ': ', &
temperatures(i), '°C'
end do
write(*,*) ''
! Example 2: Yearly sales data
write(*,*) '2. Yearly sales (indices 2020 to 2024):'
!$acc parallel loop
do year = 2020, 2024
yearly_sales(year) = real(year - 2019) * 1000.0 ! Growing sales
end do
!$acc end parallel loop
! Show sales results
do year = 2020, 2024
write(*,'(A,I0,A,F8.0)') 'Sales in ', year, ': $', yearly_sales(year)
end do
write(*,*) ''
! Example 3: Height data starting from 0
write(*,*) '3. Height data (indices 0 to 100):'
!$acc parallel loop
do i = 0, 100
height_data(i) = sqrt(real(i)) * 10.0 ! Some height formula
end do
!$acc end parallel loop
! Show some height results
write(*,*) 'Sample heights:'
do i = 0, 100, 20
write(*,'(A,I0,A,F6.2,A)') 'Height[', i, ']: ', height_data(i), ' cm'
end do
write(*,*) ''
write(*,*) 'Key lesson: Always use the CORRECT bounds in your loops!'
write(*,*) 'Array bounds: Loop bounds:'
write(*,*) 'temperatures(-10:10) → do i = -10, 10'
write(*,*) 'yearly_sales(2020:2024) → do year = 2020, 2024'
write(*,*) 'height_data(0:100) → do i = 0, 100'
end program custom_bounds
To compile this code –
nvfortran -acc -o custom_bounds custom_bounds.f90
To execute this code –
./custom_bounds
Sample output –
Working with custom array bounds in OpenACC!
1. Temperature array (indices -10 to +10):
Temperature at index -5: -7.50°C
Temperature at index -3: -4.50°C
Temperature at index -1: -1.50°C
Temperature at index 1: 1.50°C
Temperature at index 3: 4.50°C
Temperature at index 5: 7.50°C
2. Yearly sales (indices 2020 to 2024):
Sales in 2020: $ 1000.
Sales in 2021: $ 2000.
Sales in 2022: $ 3000.
Sales in 2023: $ 4000.
Sales in 2024: $ 5000.
3. Height data (indices 0 to 100):
Sample heights:
Height[0]: 0.00 cm
Height[20]: 44.72 cm
Height[40]: 63.25 cm
Height[60]: 77.46 cm
Height[80]: 89.44 cm
Height[100]: 100.00 cm
Key lesson: Always use the CORRECT bounds in your loops!
Array bounds: Loop bounds:
temperatures(-10:10) → do i = -10, 10
yearly_sales(2020:2024) → do year = 2020, 2024
height_data(0:100) → do i = 0, 100
Let us consider the another OpenACC code –
program common_mistakes
! This program shows common indexing mistakes and how to fix them
implicit none
integer, parameter :: n = 10
real :: array(n)
real :: safe_result(n)
integer :: i
! Initialize array properly
do i = 1, n
array(i) = real(i) * 2.0
end do
write(*,*) 'Original array:'
do i = 1, n
write(*,'(A,I0,A,F6.1)') 'array(', i, ') = ', array(i)
end do
write(*,*) ''
! CORRECT WAY: Proper 1-based indexing
write(*,*) 'CORRECT: Using proper 1-based indexing'
!$acc parallel loop
do i = 1, n ! ✓ Correct bounds
safe_result(i) = array(i) + 10.0
end do
!$acc end parallel loop
write(*,*) 'Results with correct indexing:'
do i = 1, 5
write(*,'(A,I0,A,F6.1)') 'safe_result(', i, ') = ', safe_result(i)
end do
write(*,*) ''
write(*,*) '=========================================='
write(*,*) 'COMMON MISTAKES TO AVOID:'
write(*,*) '=========================================='
write(*,*) ''
! Show what NOT to do (we won't actually run these dangerous loops)
write(*,*) 'MISTAKE 1: Starting from 0 (like C/Python)'
write(*,*) 'WRONG CODE: do i = 0, n-1'
write(*,*) ' - This would try to access array(0) which does not exist!'
write(*,*) ' - In Fortran, array(0) is INVALID for array(1:n)'
write(*,*) ''
write(*,*) 'MISTAKE 2: Going beyond array bounds'
write(*,*) 'WRONG CODE: do i = 1, n+1'
write(*,*) ' - This would try to access array(11) when array only has 10 elements'
write(*,*) ' - This causes memory access errors!'
write(*,*) ''
write(*,*) 'MISTAKE 3: Off-by-one errors'
write(*,*) 'WRONG CODE: do i = 0, n'
write(*,*) ' - This tries to access both array(0) and array(11)'
write(*,*) ' - Both are invalid for our array(1:10)!'
write(*,*) ''
! Demonstrate bounds checking
write(*,*) 'SAFETY TIP: Always check your bounds!'
write(*,*) 'For array(n), valid indices are: 1, 2, 3, ..., n'
write(*,'(A,I0,A,I0,A,I0)') 'Our array(', n, ') has valid indices: 1 to ', n
write(*,*) ''
write(*,*) 'REMEMBER: Fortran arrays start from 1, not 0!'
end program common_mistakes
To compile this code –
nvfortran -acc -o common_mistakes common_mistakes.f90
To execute this code –
./common_mistakes
Sample output –
Original array:
array(1) = 2.0
array(2) = 4.0
array(3) = 6.0
array(4) = 8.0
array(5) = 10.0
array(6) = 12.0
array(7) = 14.0
array(8) = 16.0
array(9) = 18.0
array(10) = 20.0
CORRECT: Using proper 1-based indexing
Results with correct indexing:
safe_result(1) = 12.0
safe_result(2) = 14.0
safe_result(3) = 16.0
safe_result(4) = 18.0
safe_result(5) = 20.0
==========================================
COMMON MISTAKES TO AVOID:
==========================================
MISTAKE 1: Starting from 0 (like C/Python)
WRONG CODE: do i = 0, n-1
- This would try to access array(0) which does not exist!
- In Fortran, array(0) is INVALID for array(1:n)
MISTAKE 2: Going beyond array bounds
WRONG CODE: do i = 1, n+1
- This would try to access array(11) when array only has 10 elements
- This causes memory access errors!
MISTAKE 3: Off-by-one errors
WRONG CODE: do i = 0, n
- This tries to access both array(0) and array(11)
- Both are invalid for our array(1:10)!
SAFETY TIP: Always check your bounds!
For array(n), valid indices are: 1, 2, 3, ..., n
Our array(10) has valid indices: 1 to 10
REMEMBER: Fortran arrays start from 1, not 0!
Click here to go back to OpenACC Fortran tutorials page.
References
- OpenACC Specification : https://www.openacc.org/specification