Working with Allocatable Arrays

Think of allocatable arrays like expandable luggage – you don’t know the exact size you’ll need until runtime, so you adjust the size dynamically. In scientific computing, problem sizes often depend on user input, file data, or computational requirements determined at runtime.

Learn dynamic memory with OpenACC, master ALLOCATE/DEALLOCATE patterns, understand variable-sized problems, and see the allocatable array lifecycle on host and device.

The Dynamic Memory Problem

Many scientific problems have unknown sizes at compile time:

Climate simulation: Grid size depends on resolution choice
Finite element: Mesh size based on geometry complexity  
Machine learning: Dataset size varies by problem
Molecular dynamics: Number of particles from input file

Visual: Static vs Allocatable Arrays

Static Arrays (Fixed Size):
Program: integer, parameter :: n = 1000
         real :: data(n)                ← Fixed at compile time
Memory:  [████████████████████████████] Always 1000 elements

Allocatable Arrays (Dynamic Size):
Program: integer :: n
         real, allocatable :: data(:)   ← Size determined at runtime
Runtime: n = get_problem_size()         
         allocate(data(n))
Memory:  [████] or [████████████████] or [████████████████████████]
         Size fits actual need!

Fortran Allocatable Array Basics

Declaration and Allocation

! Declaration - no memory allocated yet
real, allocatable :: dynamic_array(:)
integer, allocatable :: work_space(:, :)

! Allocation at runtime
integer :: problem_size, rows, cols

problem_size = 50000  ! From user input or computation
allocate(dynamic_array(problem_size))

rows = 200
cols = 300
allocate(work_space(rows, cols))

Deallocation

! Free memory when done
deallocate(dynamic_array)
deallocate(work_space)

Basic Allocatable Array with OpenACC

program basic_allocatable
  implicit none
  integer :: n, i
  real, allocatable :: data(:), result(:)

  write(*,*) 'Enter problem size:'
  read(*,*) n

  ! Allocate arrays based on user input
  allocate(data(n))
  allocate(result(n))

  ! Initialize data
  do i = 1, n
    data(i) = real(i) * 0.5
  end do

  ! OpenACC automatically handles allocatable array bounds
  !$acc parallel loop copyin(data) copyout(result)
  do i = 1, n
    result(i) = sqrt(data(i)) * 2.0 + 1.0
  end do
  !$acc end parallel loop

  write(*,*) 'Processed', n, 'elements'
  write(*,*) 'Sample result:', result(n/2)

  ! Clean up
  deallocate(data)
  deallocate(result)
end program

Visual: Allocatable Array Lifecycle

Allocatable Array Lifecycle:

1. Declaration Phase:
   Host: data(:) ← Not allocated (no memory)
   GPU:  ─       ← No corresponding memory

2. Allocation Phase:  
   Host: allocate(data(5000))
   Host: [████████████████████████████] ← 5000 elements allocated
   GPU:  ─                           ← Still no GPU memory

3. OpenACC Transfer Phase:
   !$acc parallel loop copyin(data)
   Host: [████████████████████████████] 
         ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   GPU:  [████████████████████████████] ← Auto-allocated with same size

4. Computation Phase:
   GPU:  [████████████████████████████] ← Processing on device
         (OpenACC knows the correct size automatically)

5. Transfer Back Phase:
   GPU:  [████████████████████████████] 
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
   Host: [████████████████████████████] ← Results copied back

6. Deallocation Phase:
   deallocate(data)
   Host: ─                           ← Memory freed
   GPU:  ─                           ← GPU memory auto-freed

Problem-Size Dependent Allocation

program adaptive_sizing
  implicit none
  integer :: problem_type, n, i, j
  real, allocatable :: matrix_a(:,:), matrix_b(:,:), result(:,:)

  write(*,*) 'Select problem size:'
  write(*,*) '1: Small (100x100)'
  write(*,*) '2: Medium (500x500)' 
  write(*,*) '3: Large (2000x2000)'
  read(*,*) problem_type

  ! Determine size based on problem type
  select case(problem_type)
    case(1)
      n = 100
    case(2) 
      n = 500
    case(3)
      n = 2000
    case default
      n = 100
  end select

  write(*,'(A,I0,A,I0)') 'Allocating ', n, '×', n, ' matrices'

  ! Allocate matrices of appropriate size
  allocate(matrix_a(n, n))
  allocate(matrix_b(n, n))
  allocate(result(n, n))

  ! Initialize matrices
  do j = 1, n
    do i = 1, n
      matrix_a(i, j) = real(i + j) * 0.01
      matrix_b(i, j) = real(i * j) * 0.001
    end do
  end do

  ! Process with OpenACC - automatic size detection
  !$acc parallel loop collapse(2) copyin(matrix_a, matrix_b) copyout(result)
  do j = 1, n
    do i = 1, n
      result(i, j) = matrix_a(i, j) + matrix_b(i, j) * 2.0
    end do
  end do
  !$acc end parallel loop

  write(*,*) 'Computation complete'
  write(*,*) 'Sample result:', result(n/2, n/2)

  deallocate(matrix_a)
  deallocate(matrix_b)
  deallocate(result)
end program

File-Based Dynamic Allocation

program file_based_allocation
  implicit none
  integer :: n, i, iostat
  real, allocatable :: input_data(:), processed_data(:)

  ! Read problem size from file header
  open(10, file='problem_data.txt', status='old', iostat=iostat)
  if (iostat /= 0) then
    write(*,*) 'Creating sample data file...'
    ! Create sample file for demo
    open(10, file='problem_data.txt', status='replace')
    write(10,*) 7500  ! Problem size
    do i = 1, 7500
      write(10,*) sin(real(i) * 0.001) * 100.0
    end do
    close(10)
    open(10, file='problem_data.txt', status='old')
  end if

  ! Read size and allocate accordingly  
  read(10,*) n
  write(*,'(A,I0,A)') 'File contains ', n, ' data points'

  allocate(input_data(n))
  allocate(processed_data(n))

  ! Read all data
  do i = 1, n
    read(10,*) input_data(i)
  end do
  close(10)

  write(*,*) 'Data loaded, processing on GPU...'

  ! Process with OpenACC
  !$acc parallel loop copyin(input_data) copyout(processed_data)
  do i = 1, n
    processed_data(i) = abs(input_data(i)) + sqrt(abs(input_data(i))) * 0.5
  end do
  !$acc end parallel loop

  write(*,*) 'Processing complete'
  write(*,*) 'First result:', processed_data(1)
  write(*,*) 'Last result:', processed_data(n)

  deallocate(input_data)
  deallocate(processed_data)
end program

Allocatable Arrays in Data Regions

program allocatable_data_regions
  implicit none
  integer :: n, operation, i
  real, allocatable :: work_array(:), temp_1(:), temp_2(:)

  n = 10000
  write(*,'(A,I0,A)') 'Working with ', n, ' element arrays'

  ! Allocate all working arrays
  allocate(work_array(n))
  allocate(temp_1(n))
  allocate(temp_2(n))

  ! Initialize
  do i = 1, n
    work_array(i) = cos(real(i) * 0.001) * 50.0
  end do

  ! Multiple operations in data region with allocatable arrays
  !$acc data copy(work_array) create(temp_1, temp_2)

    write(*,*) 'Performing multiple operations in data region...'

    ! Operation 1: Square and scale
    !$acc parallel loop
    do i = 1, n
      temp_1(i) = work_array(i) * work_array(i) * 0.01
    end do
    !$acc end parallel loop

    ! Operation 2: Apply function
    !$acc parallel loop
    do i = 1, n
      temp_2(i) = sqrt(abs(temp_1(i))) + 2.0
    end do
    !$acc end parallel loop

    ! Operation 3: Combine results back to work_array
    !$acc parallel loop
    do i = 1, n
      work_array(i) = temp_1(i) + temp_2(i) * 0.5
    end do
    !$acc end parallel loop

    write(*,*) '✓ All operations completed with arrays resident on GPU'

  !$acc end data

  write(*,*) 'Final result sample:', work_array(5000)

  deallocate(work_array)
  deallocate(temp_1)
  deallocate(temp_2)
end program

Dynamic Reallocation Pattern

program dynamic_reallocation
  implicit none
  integer :: current_size, new_size, i
  real, allocatable :: growing_array(:)
  real :: growth_factor = 1.5

  ! Start with small array
  current_size = 1000
  allocate(growing_array(current_size))

  ! Initialize
  do i = 1, current_size
    growing_array(i) = real(i)
  end do

  write(*,'(A,I0)') 'Initial array size: ', current_size

  ! Process current array
  !$acc parallel loop copy(growing_array)
  do i = 1, current_size
    growing_array(i) = growing_array(i) * 2.0 + 1.0
  end do
  !$acc end parallel loop

  ! Simulate need for larger array
  new_size = int(current_size * growth_factor)
  write(*,'(A,I0)') 'Reallocating to size: ', new_size

  block
    real :: temp_data(current_size)

    ! Save current data
    temp_data = growing_array(1:current_size)

    ! Reallocate with new size
    deallocate(growing_array)
    allocate(growing_array(new_size))

    ! Restore existing data and initialize new elements
    growing_array(1:current_size) = temp_data
    do i = current_size + 1, new_size
      growing_array(i) = real(i) * 0.5  ! Initialize new elements
    end do
  end block

  current_size = new_size

  ! Process expanded array  
  !$acc parallel loop copy(growing_array)
  do i = 1, current_size
    growing_array(i) = sqrt(abs(growing_array(i)))
  end do
  !$acc end parallel loop

  write(*,*) 'Array successfully expanded and processed'
  write(*,*) 'Final size:', current_size
  write(*,*) 'Sample values:', growing_array(1), growing_array(current_size)

  deallocate(growing_array)
end program

Multi-dimensional Allocatable Arrays

program multidim_allocatable
  implicit none
  integer :: nx, ny, nz, i, j, k
  real, allocatable :: field_3d(:,:,:), result_3d(:,:,:)

  ! Get dimensions from user or computation
  nx = 50
  ny = 60  
  nz = 40

  write(*,'(A,I0,A,I0,A,I0,A)') 'Allocating 3D array: ', nx, '×', ny, '×', nz

  allocate(field_3d(nx, ny, nz))
  allocate(result_3d(nx, ny, nz))

  ! Initialize 3D field
  do k = 1, nz
    do j = 1, ny
      do i = 1, nx
        field_3d(i, j, k) = real(i + j + k) * 0.1
      end do
    end do
  end do

  ! 3D processing with OpenACC
  !$acc parallel loop collapse(3) copyin(field_3d) copyout(result_3d)
  do k = 1, nz
    do j = 1, ny
      do i = 1, nx
        result_3d(i, j, k) = field_3d(i, j, k) + &
                           sin(field_3d(i, j, k) * 0.1) * 10.0
      end do
    end do
  end do
  !$acc end parallel loop

  write(*,*) '3D processing complete'
  write(*,*) 'Center point result:', result_3d(nx/2, ny/2, nz/2)

  deallocate(field_3d)
  deallocate(result_3d)
end program

Error Handling with Allocatable Arrays

program safe_allocation
  implicit none
  integer :: n, alloc_status, i
  real, allocatable :: safe_array(:)
  character(len=100) :: error_msg

  n = 100000000  ! Large size that might fail

  write(*,'(A,I0,A)') 'Attempting to allocate ', n, ' elements'

  allocate(safe_array(n), stat=alloc_status, errmsg=error_msg)

  if (alloc_status /= 0) then
    write(*,*) 'Allocation failed:', trim(error_msg)
    write(*,*) 'Trying smaller size...'

    n = n / 10  ! Try smaller size
    allocate(safe_array(n), stat=alloc_status)

    if (alloc_status /= 0) then
      write(*,*) 'Still failed - using minimal size'
      n = 1000
      allocate(safe_array(n))
    end if
  end if

  write(*,'(A,I0,A)') 'Successfully allocated ', n, ' elements'

  ! Initialize and process
  do i = 1, n
    safe_array(i) = real(i) * 0.001
  end do

  !$acc parallel loop copy(safe_array)
  do i = 1, n
    safe_array(i) = safe_array(i) * safe_array(i) + 1.0
  end do
  !$acc end parallel loop

  write(*,*) 'Processing successful'
  write(*,*) 'Sample result:', safe_array(min(1000, n))

  if (allocated(safe_array)) then
    deallocate(safe_array)
    write(*,*) 'Memory properly deallocated'
  end if
end program

Performance Considerations

Memory Allocation Overhead

Stack arrays (fixed):     Near-zero allocation time
Allocatable arrays:       Small overhead per allocation
Frequent reallocation:    Performance cost - avoid if possible
Large allocations:        May require memory defragmentation

Best Practices Table

ScenarioRecommendationReason
Known size rangePre-allocate maximum sizeAvoid reallocation overhead
Unknown sizeAllocate once, process in chunksBalance memory and performance
Growing arraysUse growth factor (1.5x – 2x)Reduce number of reallocations
File-basedRead size first, allocate onceSingle allocation is efficient

Visual: OpenACC Automatic Size Detection

OpenACC with Allocatable Arrays:

Fortran Code:
  integer :: n = 5000
  real, allocatable :: data(:)
  allocate(data(n))

OpenACC automatically knows:
┌─────────────────────────────────────────────────────────────┐
│ Array Information                                           │
├─────────────────────────────────────────────────────────────┤
│ Name: data                                                  │
│ Lower bound: 1         (Fortran default)                    │
│ Upper bound: 5000      (from allocation)                    │
│ Size: 5000 elements    (automatically computed)             │
│ Memory needed: 20KB    (5000 × 4 bytes per real)            │
└─────────────────────────────────────────────────────────────┘

No need to specify bounds in data clauses:
✓ copyin(data)     ← OpenACC uses full allocated size
✗ copyin(data(1:n)) ← Unnecessary (but valid)

Common Patterns

Pattern 1: User-Input Sizing

write(*,*) 'Enter problem size:'
read(*,*) n
allocate(arrays(n))
! Process with OpenACC
deallocate(arrays)

Pattern 2: File-Header Sizing

open(file='data.txt')
read(*,*) n  ! First line contains size
allocate(data(n))
! Read and process data
close(file)
deallocate(data)

Pattern 3: Conditional Sizing

if (high_precision) then
  allocate(data(large_size))
else
  allocate(data(normal_size))  
end if

Best Practices

DO:

  • Check allocation status for large arrays
  • Deallocate arrays when finished
  • Use allocatable arrays for unknown problem sizes
  • Let OpenACC automatically detect array bounds
  • Pre-allocate maximum expected size when possible

DON’T:

  • Forget to deallocate (memory leaks)
  • Reallocate frequently in loops
  • Assume allocation always succeeds
  • Mix fixed and allocatable arrays unnecessarily
  • Allocate inside parallel regions

Quick Reference

! Declaration:
real, allocatable :: array(:)

! Allocation:
allocate(array(size))

! OpenACC usage:
!$acc parallel loop copyin(array) copyout(result)
! OpenACC automatically knows array bounds

! Deallocation:
deallocate(array)

! Safety check:
if (allocated(array)) deallocate(array)

Quick Summary

=== Allocatable Arrays Concepts Summary ===

Fortran Allocatable Array Operations:
┌─────────────────────────┬────────────────────────────────────────────────┐
│ Operation               │ Purpose                                        │
├─────────────────────────┼────────────────────────────────────────────────┤
│ Declaration             │ real, allocatable :: array(:)                  │
│ Allocation              │ allocate(array(size))                          │
│ Size Query              │ size(array) or ubound(array,1)                 │
│ Status Check            │ allocated(array)                               │
│ Deallocation            │ deallocate(array)                              │
│ Error Handling          │ allocate(array(n), stat=status)                │
└─────────────────────────┴────────────────────────────────────────────────┘

OpenACC Automatic Size Detection:
┌─────────────────────────┬────────────────────────────────────────────────┐
│ Array Type              │ OpenACC Behavior                               │
├─────────────────────────┼────────────────────────────────────────────────┤
│ Fixed Size              │ array(1:100) - bounds known at compile time    │
│ Allocatable 1D          │ array(:) - bounds detected automatically       │
│ Allocatable 2D          │ matrix(:,:) - all dimensions auto-detected     │
│ Allocatable 3D          │ field(:,:,:) - full shape auto-detected        │
│ Variable Bounds         │ Works with any allocated size at runtime       │
└─────────────────────────┴────────────────────────────────────────────────┘

Memory Allocation Patterns:
┌─────────────────────────┬────────────────────────────────────────────────┐
│ Scenario                │ Best Practice                                  │
├─────────────────────────┼────────────────────────────────────────────────┤
│ User Input Size         │ Read size, allocate once, process              │
│ File-based Size         │ Read header, allocate based on data size       │
│ Problem-dependent       │ Calculate size, allocate appropriate arrays    │
│ Multi-resolution        │ Allocate different sizes for different levels  │
│ Growing Arrays          │ Reallocate with growth factor (1.5x - 2x)      │
└─────────────────────────┴────────────────────────────────────────────────┘

Performance Characteristics:
┌─────────────────────────┬──────────────────┬──────────────────────────────┐
│ Array Type              │ Allocation Cost  │ OpenACC Transfer Efficiency  │
├─────────────────────────┼──────────────────┼──────────────────────────────┤
│ Small arrays (<1MB)     │ Negligible       │ Fast                         │
│ Medium arrays (1-100MB) │ Small            │ Good                         │
│ Large arrays (>100MB)   │ Moderate         │ Bandwidth-limited            │
│ 3D arrays               │ Higher           │ Excellent with collapse(3)   │
│ Frequent reallocation   │ Expensive        │ Avoid in performance code    │
└─────────────────────────┴──────────────────┴──────────────────────────────┘

Common Allocatable Array Use Cases:
• Scientific simulations with problem-size input parameters
• File-based data processing with unknown data sizes
• Multi-grid methods with different resolution levels
• Adaptive mesh refinement (AMR) applications
• Monte Carlo simulations with varying sample sizes
• Image/signal processing with different image dimensions
• Finite element codes with mesh-dependent array sizes

Error Handling Best Practices:
┌─────────────────────────────────────────────────────────────────────────────┐
│ ! Safe allocation with error checking                                       │
│ allocate(array(n), stat=alloc_status, errmsg=error_message)                 │
│ if (alloc_status /= 0) then                                                 │
│   write(*,*) 'Allocation failed:', trim(error_message)                      │
│   ! Handle error (reduce size, exit gracefully, etc.)                       │
│ end if                                                                      │
│                                                                             │
│ ! Always check before deallocation                                          │
│ if (allocated(array)) deallocate(array)                                     │
└─────────────────────────────────────────────────────────────────────────────┘

OpenACC Data Clause Usage:
• copyin(allocatable_array)    - Transfer full allocated array to device
• copyout(allocatable_array)   - Transfer full allocated array from device
• copy(allocatable_array)      - Bidirectional transfer of full array
• create(allocatable_array)    - Allocate on device with same bounds

Advantages of Allocatable Arrays:
✓ Memory usage matches actual problem requirements
✓ Runtime flexibility for different problem sizes
✓ OpenACC handles bounds detection automatically
✓ Works seamlessly with data regions and async operations
✓ Perfect for scientific codes with variable parameters
✓ Enables efficient memory management in GPU applications

Example Code

Let us consider the following OpenACC code –

program allocatable_demo
  ! Demonstrates OpenACC with Fortran allocatable arrays
  
  implicit none
  
  integer :: problem_size, demo_choice, i, j, k
  integer :: alloc_status, matrix_size, nx, ny, nz, work_size
  real, allocatable :: dynamic_array(:), result_array(:)
  real, allocatable :: matrix_work(:,:), matrix_result(:,:)
  real, allocatable :: field_3d(:,:,:)
  real, allocatable :: work_data(:), temp_result(:), final_output(:)
  
  write(*,*) 'OpenACC with Fortran Allocatable Arrays'
  write(*,*) '====================================='
  write(*,*) ''
  
  write(*,*) 'Demonstration 1: Basic Dynamic Allocation'
  write(*,*) '(Array size determined at runtime)'
  write(*,*) ''
  
  ! Get problem size from user simulation
  problem_size = 8000  ! Simulating user input
  write(*,'(A,I0,A)') 'Simulated user input: problem size = ', problem_size, ' elements'
  
  ! Allocate arrays dynamically
  allocate(dynamic_array(problem_size), stat=alloc_status)
  if (alloc_status /= 0) then
    write(*,*) 'ERROR: Failed to allocate dynamic_array'
    stop
  end if
  
  allocate(result_array(problem_size), stat=alloc_status)
  if (alloc_status /= 0) then
    write(*,*) 'ERROR: Failed to allocate result_array'
    stop
  end if
  
  write(*,'(A,I0,A)') '✓ Successfully allocated arrays with ', problem_size, ' elements'
  
  ! Initialize with data
  do i = 1, problem_size
    dynamic_array(i) = sin(real(i) * 0.0001) * 100.0 + real(i) * 0.01
  end do
  
  write(*,*) 'Data initialized on host'
  write(*,'(A,F8.4)') 'Sample input: ', dynamic_array(1000)
  
  ! Process with OpenACC - automatic size detection
  !$acc parallel loop copyin(dynamic_array) copyout(result_array)
  do i = 1, problem_size
    result_array(i) = sqrt(abs(dynamic_array(i))) + dynamic_array(i) * 0.5
  end do
  !$acc end parallel loop
  
  write(*,*) '✓ OpenACC processing complete'
  write(*,'(A,F8.4)') 'Sample result: ', result_array(1000)
  write(*,*) '✓ OpenACC automatically detected array bounds'
  write(*,*) ''
  
  ! Deallocate first arrays
  deallocate(dynamic_array)
  deallocate(result_array)
  write(*,*) '✓ Arrays deallocated'
  write(*,*) ''
  
  write(*,*) 'Demonstration 2: Problem-Size Dependent Allocation'
  write(*,*) '(Different matrix sizes based on problem type)'
  write(*,*) ''
  
  ! Simulate different problem types
  demo_choice = 2  ! Simulating medium problem selection
  
  select case(demo_choice)
    case(1)
      matrix_size = 200
      write(*,*) 'Selected: Small problem (200×200 matrix)'
    case(2)
      matrix_size = 500
      write(*,*) 'Selected: Medium problem (500×500 matrix)'
    case(3)
      matrix_size = 1000
      write(*,*) 'Selected: Large problem (1000×1000 matrix)'
    case default
      matrix_size = 200
  end select
  
  ! Allocate matrices of appropriate size
  allocate(matrix_work(matrix_size, matrix_size))
  allocate(matrix_result(matrix_size, matrix_size))
  
  write(*,'(A,I0,A,I0,A)') '✓ Allocated ', matrix_size, '×', matrix_size, ' matrices'
  
  ! Initialize matrix
  do j = 1, matrix_size
    do i = 1, matrix_size
      matrix_work(i, j) = real(i + j) * 0.001 + cos(real(i * j) * 0.0001)
    end do
  end do
  
  write(*,*) 'Matrix initialized on host'
  
  ! Process matrix with OpenACC
  !$acc parallel loop collapse(2) copyin(matrix_work) copyout(matrix_result)
  do j = 1, matrix_size
    do i = 1, matrix_size
      matrix_result(i, j) = matrix_work(i, j) * matrix_work(i, j) + &
                          sin(matrix_work(i, j)) * 0.5
    end do
  end do
  !$acc end parallel loop
  
  write(*,*) '✓ Matrix processing complete'
  write(*,'(A,F8.4)') 'Center element result: ', matrix_result(matrix_size/2, matrix_size/2)
  write(*,*) '✓ OpenACC handled 2D allocatable array automatically'
  write(*,*) ''
  
  ! Clean up matrices
  deallocate(matrix_work)
  deallocate(matrix_result)
  
  write(*,*) 'Demonstration 3: 3D Allocatable Array Processing'
  write(*,*) '(Scientific field simulation with dynamic dimensions)'
  write(*,*) ''
  
  ! 3D field dimensions
  nx = 40
  ny = 50
  nz = 30
  
  write(*,'(A,I0,A,I0,A,I0,A)') 'Allocating 3D field: ', nx, '×', ny, '×', nz
  
  allocate(field_3d(nx, ny, nz))
  write(*,*) '✓ 3D array allocated successfully'
  
  ! Initialize 3D field
  do k = 1, nz
    do j = 1, ny
      do i = 1, nx
        field_3d(i, j, k) = real(i + j + k) * 0.01 + &
                          sin(real(i) * 0.1) * cos(real(j) * 0.1) * sin(real(k) * 0.1)
      end do
    end do
  end do
  
  write(*,*) '3D field initialized'
  write(*,'(A,F8.4)') 'Sample field value: ', field_3d(20, 25, 15)
  
  ! Process 3D field with OpenACC
  !$acc parallel loop collapse(3) copy(field_3d)
  do k = 2, nz-1
    do j = 2, ny-1
      do i = 2, nx-1
        ! 3D smoothing operation (simple Laplacian)
        field_3d(i, j, k) = 0.9 * field_3d(i, j, k) + 0.1 * ( &
          field_3d(i-1, j, k) + field_3d(i+1, j, k) + &
          field_3d(i, j-1, k) + field_3d(i, j+1, k) + &
          field_3d(i, j, k-1) + field_3d(i, j, k+1)) / 6.0
      end do
    end do
  end do
  !$acc end parallel loop
  
  write(*,*) '✓ 3D field smoothing complete'
  write(*,'(A,F8.4)') 'Smoothed field value: ', field_3d(20, 25, 15)
  write(*,*) '✓ OpenACC processed 3D allocatable array efficiently'
  write(*,*) ''
  
  deallocate(field_3d)
  
  write(*,*) 'Demonstration 4: Allocatable Arrays in Data Regions'
  write(*,*) '(Multiple operations with persistent allocatable arrays)'
  write(*,*) ''
  
  ! Allocate working arrays
  work_size = 12000
  
  allocate(work_data(work_size))
  allocate(temp_result(work_size))
  allocate(final_output(work_size))
  
  write(*,'(A,I0,A)') '✓ Allocated working arrays with ', work_size, ' elements'
  
  ! Initialize working data
  do i = 1, work_size
    work_data(i) = real(i) * 0.001 + cos(real(i) * 0.0005) * 10.0
  end do
  
  ! Multiple operations in data region
  !$acc data copyin(work_data) copyout(final_output) create(temp_result)
  
    write(*,*) '→ Arrays transferred to GPU (allocatable bounds auto-detected)'
    
    ! Step 1: Initial processing
    !$acc parallel loop
    do i = 1, work_size
      temp_result(i) = work_data(i) * work_data(i) * 0.01
    end do
    !$acc end parallel loop
    
    write(*,*) '✓ Step 1 complete: squared and scaled'
    
    ! Step 2: Apply mathematical function
    !$acc parallel loop
    do i = 1, work_size
      temp_result(i) = sqrt(abs(temp_result(i))) + exp(temp_result(i) * 0.001)
    end do
    !$acc end parallel loop
    
    write(*,*) '✓ Step 2 complete: applied mathematical functions'
    
    ! Step 3: Final combination
    !$acc parallel loop
    do i = 1, work_size
      final_output(i) = work_data(i) * 0.6 + temp_result(i) * 0.4
    end do
    !$acc end parallel loop
    
    write(*,*) '✓ Step 3 complete: final combination'
    write(*,*) '← Final results transferred back to host'
    
  !$acc end data
  
  write(*,'(A,F8.4)') 'Final processing result: ', final_output(6000)
  write(*,*) '✓ All operations performed with arrays resident on GPU'
  write(*,*) ''
  
  ! Clean up
  deallocate(work_data)
  deallocate(temp_result)  
  deallocate(final_output)
  
  write(*,*) 'Key Benefits of Allocatable Arrays with OpenACC:'
  write(*,*) '=============================================='
  write(*,*) '• Problem size determined at runtime'
  write(*,*) '• Memory usage optimized for actual needs'
  write(*,*) '• OpenACC automatically detects array bounds'
  write(*,*) '• No need to specify array sizes in data clauses'
  write(*,*) '• Works seamlessly with 1D, 2D, and 3D arrays'
  write(*,*) '• Perfect for file-based or user-input problem sizes'
  write(*,*) ''
  write(*,*) 'Memory Management:'
  write(*,*) '• ALLOCATE creates arrays at runtime'
  write(*,*) '• DEALLOCATE frees memory when finished'
  write(*,*) '• Use STAT= to check allocation success'
  write(*,*) '• OpenACC handles device memory automatically'
  write(*,*) ''
  write(*,*) 'Best Use Cases:'
  write(*,*) '• Scientific simulations with variable problem sizes'
  write(*,*) '• File-based data processing'
  write(*,*) '• User-configurable problem parameters'
  write(*,*) '• Multi-resolution computations'
  write(*,*) '• Adaptive algorithms with growing data structures'
  
end program allocatable_demo

To compile this code –

nvfortran -acc -Minfo=accel -O2 allocatable_demo.f90 -o allocatable_demo

To execute this code –

./allocatable_demo

Sample output –

 
 OpenACC with Fortran Allocatable Arrays
 =====================================
 
 Demonstration 1: Basic Dynamic Allocation
 (Array size determined at runtime)
 
Simulated user input: problem size = 8000 elements
✓ Successfully allocated arrays with 8000 elements
 Data initialized on host
Sample input:  19.9833
 ✓ OpenACC processing complete
Sample result:  14.4619
 ✓ OpenACC automatically detected array bounds
 
 ✓ Arrays deallocated
 
 Demonstration 2: Problem-Size Dependent Allocation
 (Different matrix sizes based on problem type)
 
 Selected: Medium problem (500×500 matrix)
✓ Allocated 500×500 matrices
 Matrix initialized on host
 ✓ Matrix processing complete
Center element result:   2.7471
 ✓ OpenACC handled 2D allocatable array automatically
 
 Demonstration 3: 3D Allocatable Array Processing
 (Scientific field simulation with dynamic dimensions)
 
Allocating 3D field: 40×50×30
 ✓ 3D array allocated successfully
 3D field initialized
Sample field value:  -0.1267
 ✓ 3D field smoothing complete
Smoothed field value:  -0.1263
 ✓ OpenACC processed 3D allocatable array efficiently
 
 Demonstration 4: Allocatable Arrays in Data Regions
 (Multiple operations with persistent allocatable arrays)
 
✓ Allocated working arrays with 12000 elements
 → Arrays transferred to GPU (allocatable bounds auto-detected)
 ✓ Step 1 complete: squared and scaled
 ✓ Step 2 complete: applied mathematical functions
 ✓ Step 3 complete: final combination
 ← Final results transferred back to host
Final processing result:  -1.7839
 ✓ All operations performed with arrays resident on GPU
 
 Key Benefits of Allocatable Arrays with OpenACC:
 ==============================================
 • Problem size determined at runtime
 • Memory usage optimized for actual needs
 • OpenACC automatically detects array bounds
 • No need to specify array sizes in data clauses
 • Works seamlessly with 1D, 2D, and 3D arrays
 • Perfect for file-based or user-input problem sizes
 
 Memory Management:
 • ALLOCATE creates arrays at runtime
 • DEALLOCATE frees memory when finished
 • Use STAT= to check allocation success
 • OpenACC handles device memory automatically
 
 Best Use Cases:
 • Scientific simulations with variable problem sizes
 • File-based data processing
 • User-configurable problem parameters
 • Multi-resolution computations
 • Adaptive algorithms with growing data structures

Click here to go back to OpenACC Fortran tutorials page.

References


Mandar Gurav Avatar

Mandar Gurav

Parallel Programmer, Trainer and Mentor


If you are new to Parallel Programming you can start here.



Beginner CUDA Fortran Hello World Message Passing Interface MPI Nvidia Nsight Systems NVPROF OpenACC OpenACC Fortran OpenMP PGI Fortran Compiler Profiling Vector Addition


Popular Categories