0

Fortran runtime-error 'memory allocation failed' occurs while I'm only asking for about 100 real(8)'s of memory.

I'm writing some code in Fortran. I used several (about ten) allocatable real(8) arrays. When I allocate them, my command line shows me 'Memory allocation failed'. But I'm only allocating their sizes to about ten. I've briefly checked my computer and I don't think it's in a condition which cannot handle 100 float-point numbers.

The code is something like this:

module m
contains

function foo (a, b, c) result (...)
implicit none
  real(8), allocatable, intent(inout) :: a(:), b(:), c(:)
  allocate(a(0:10)) ! Actual size include an integer n passed to here. I'm pretty sure n is no greater then 10
  allocate(b(1:10))
  allocate(c(0:9))
  ! Some calculation
  a_variable = bar(...)
endfunction foo 

function bar (...) result (x)
implicit none
  real(8), allocatable :: x(:)
  allocate(x(8)) ! This is line X
  ! Some calculation
endfunction bar

endmodule m

program testm
use m
implicit none
  ! Some code that calls foo
endprogram testm

When I compile and run the code, the command line tells me 'Memory allocation failed' and an error message from the operating system (Windows 10) 'Not enough memory resources to complete this command'. That's really weird since I'm only asking for about 100 float-point numbers of memory.

Something even more puzzling happens when I try to ensure that I didn't pass unexpectedly a big number to n.

When I add a print*, n to line X, the exception stays and the indicated line remains the same line X, which is now the print code!

I'm just completely puzzled what kind of bug could this be. Since I'm a beginner in Fortran, I'm not sure if I've made any undefined behaviors here.

I'm using g95 to compile a .f90 file on Windows 10.

Here's the complete code:

module SplineInterp
! MODULE COMMENT
! Cubic Spline Interpolation Module
! Does Cubic Spline Interpolation with natural boundary conditions
! All calculation is made in real(8). Parameter rk = 8 is defined for real kind specifications.
! In all places where an unexpected zero on the denominator appears, the program will stop with a message,
!  usually "The matrix is singular!". A number n is considered to be zero if dabs(n) < e_min, e_min is a
!  pre-defined parameter.

integer, parameter :: rk = 8
real(rk), parameter :: e_min = 1d-5

contains

function CH_SOLVE (a, b, c, f, n) result (x)
implicit none
! FUNCTION COMMENT
! Thomas Chasing Method, solves a tri-diagonal-matrix linear equation set Ax=f
! First does LU-decomposition and then solves the upper- and lower-triangular equations
! ARGUMENTS:
!   a(2:n), b(1:n), c(1:n-1) :: elements of the coefficient matrix A. See lecture notes for convention
!   f(1:n) :: the inhomogeneous term column-vector in the equation
!   n :: size of coefficient matrix
! RETURNS:
!   x :: solution to the equation

    real(rk), allocatable, intent(inout) :: a(:)
    real(rk), allocatable, intent(inout) :: b(:)
    real(rk), allocatable, intent(in) :: c(:)
    real(rk), allocatable, intent(inout) :: f(:)
    integer, intent(in) :: n
    ! Iteration variable
    integer :: i
    real(rk), allocatable :: x(:)
    print*, n
    allocate(x(n))
    ! LU-decomposition
    ! because L has only one non-trivial diagonal line, we can multiply L^-1 to f during the process
    ! See lecture notes for formulae used
    do i = 2, n
        if (dabs(b(i - 1)) < e_min) then
            stop "The matrix is singular!"
        endif
        ! Calculate elements of L
        a(i) = a(i) / b(i - 1)
        ! Calculate elements of U
        b(i) = b(i) - a(i) * c(i - 1)
        ! Multiply L^-1 to f
        f(i) = f(i) - a(i) * f(i - 1)
    enddo
    if (dabs(b(n)) < e_min) then
        stop "The matrix is singular!"
    endif
    ! Solve Ux=(L^-1 f) using front-iteration method
    ! See lecture notes for formulae used
    x(n) = f(n) / b(n)
    do i = n - 1, 1, -1
        if (dabs(b(i)) < e_min) then
            stop "The matrix is singular!"
        endif
        x(i) = (f(i) - c(i) * x(i + 1)) / b(i)
    enddo

endfunction CH_SOLVE

function interp (x, y, n, xi) result (yi)
implicit none
! FUNCTION COMMENT
! Interpolates the values of y at points xi based on the given data points (x, y)
! Using Cubic-Spline Interpolation, solving the intermediate equation set with Thomas Method
! ARGUMENTS:
!   x, y :: Known data points, should be one-dimensional arrays of same shape (0:n)
!   n :: size of given datas points
!   xi :: The points at which the interpolation is to be evaluated (assumed to be in increasing order)
! RETURNS:
!   yi :: Interpolation results, an array with the same size of xi

    real(rk), allocatable, intent(in) :: x(:), y(:), xi(:)
    integer, intent(in) :: n
    real(rk), allocatable :: yi(:)
    ! Intermediate variables
    ! h(0:n-1) :: length of intervals
    ! M(0:n) :: to be solved, second-order-derivatives of spline function at data points
    ! mu(2:n-1), l(1:n-2), diag2(1:n-1) :: coefficients in the tri-diagonal-matrix
    ! d(1:n-1) :: non-homogeneous term in the linear equation set
    real(rk), allocatable :: h(:), M(:), mu(:), l(:), diag2(:), d(:)
    ! Iteration variable
    integer :: i, j
    print*, n
    allocate(yi(size(xi)))
    allocate(h(0 : n-1))
    allocate(M(0 : n))
    allocate(mu(2 : n-1))
    allocate(l(1 : n-2))
    allocate(diag2(1 : n-1))
    allocate(d(1 : n-1))
    ! Construction of linear equation set for M
    ! Calculation of h, m & d, see lecture notes for formulae used
    h(1) = x(2) - x(1)
    do i = 2, n-1
        h(i) = x(i + 1) - x(i)
        ! Used a trick here, calculate d(i) in two parts to save calculation time
        d(i+1) = - (y(i + 1) - y(i)) / h(i)
        d(i) = (d(i) - d(i + 1)) * 6 / (h(i - 1) + h(i))
        mu(i) = h(i - 1) / (h(i - 1) + h(i))
        if (i <= n-2) then
            l(i) = h(i) / (h(i - 1) + h(i))
        endif
    enddo
    ! Do Thomas Method solving
    diag2 = 2 ! Diagonal elements of coefficient matrix are all 2
    M(1 : n - 1) = CH_SOLVE(mu, diag2, l, d, n-1)
    ! Boundary conditions
    M(0) = 0
    M(n) = 0
    ! Do evaluation
    ! Here j represents the index of interval where the current xi points is
    j = 0
    ! Interpolation point is invalid if it's smaller than the smallest x
    if (xi(1) < x(0) - e_min) then
        stop "Invalid interpolation point!"
    endif
    do i = 1, size(xi)
        ! Locate the interval where xi(i) is
        do while (xi(i) > x(j + 1) + e_min)
            j = j + 1
            ! Invalid interpolation point if xi(i) > x(n)
            if (j >= n) then
                stop "Invalid interpolation point!"
            endif
        enddo
        ! See lecture notes for this formula
        yi(i) = (x(j+1)-xi(i))**3*M(j)/(6*h(j))&
                +(xi(i)-x(j))**3*M(j+1)/(6*h(j))&
                +(y(j)-M(j)*h(j)**2/6)*(x(j+1)-xi(i))/h(j)&
                +(y(j+1)-M(j+1)*h(j)**2/6)*(xi(i)-x(j))/h(j)
    enddo

endfunction interp

endmodule SplineInterp

program DoSplineInterp
use SplineInterp
implicit none
! PROGRAM COMMENT
! A program to use to above algorithm to interpole the profile of an airplane wing

    real(rk), allocatable :: x(:), y(:), xi(:), yi(:)
    ! Iteration variable
    integer :: i
    integer, parameter :: n = 9
    allocate(x(0:9))
    allocate(y(0:9))
    allocate(xi(151))
    allocate(yi(151))
    x = (/0d0, 3d0, 5d0, 7d0, 9d0, 11d0, 12d0, 13d0, 14d0, 15d0/)
    y = (/0d0, 1.2d0, 1.7d0, 2d0, 2.1d0, 2d0, 1.8d0, 1.2d0, 1d0, 1.6d0/)
    ! xi are evenly-distributed points with interval 0.1 between 0 and 15
    do i = 1, 151
        xi(i) = (i - 1) * 0.1
    enddo
    yi = interp(x, y, n, xi)
    ! Write results to file
    open (1, file = "Spline.txt", status = "new")
    write (1, *) "x:"
    write (1, *) x
    write (1, *) "y:"
    write (1, *) y
    write (1, *) "xi:"
    write (1, *) xi
    write (1, *) "yi:"
    write (1, *) yi
    close(1)

endprogram DoSplineInterp
Thomas
  • 1
  • 1
    Please compile with your compiler's error checking options. – francescalus Mar 26 '19 at 13:52
  • 2
    Indeed: `d(i+1)` in the loop `do i = 2, n-1` when `allocate(d(1 : n-1))`. – francescalus Mar 26 '19 at 13:54
  • Welcome, be sure to take the welcome [tour]. This kinds of weird errors have often the same origin, memory corruption. You can easily find many of them by using your compiler's features. G95 is an old and unsupported compiler, but even in this compiler you can use `g95 -g -fbounds-check`. In gfortran you can use `gfortran -g -fcheck=all` or `gfortran -g -fsanitize=address`. This way the code will be slightly slower, but you will find many errors quickly without the need to ask. note that `real(8)` is ugly and not portable https://stackoverflow.com/questions/838310/fortran-90-kind-parameter – Vladimir F Героям слава Mar 26 '19 at 14:40
  • A check with valgrind --tool=memcheck ./a.out on linux shows a few invalid read/write and conditional jump depends on uninitialized values. @francescalus has point out one of them. – KL-Yang Mar 27 '19 at 06:36
  • Problem solved! Thanks for pointing out the problem (indeed ```d(i+1)``` call) and kindly reminder about compiler and data type (changed them already). – Thomas Mar 27 '19 at 07:10

0 Answers0