Подпрограмма Fortran забывает выделяемое значение элемента массива после многих итераций

У меня есть программа на Фортране, структура которой приведена ниже. Эта программа вылетала из-за ошибки «Ошибка сегментации», поэтому я много копался в первопричине.

Оказывается, в подпрограмме SUB1, когда переменная счетчика цикла DO I достигает значения 187606, значение Z(608673), которое изначально было прочитано как 28,29 из внешнего файла, каким-то необъяснимым образом меняется на 3.9809702045652999E-309, хотя не было вычислений, которые изменили бы значения любого из массивов X, Y или Z после чтения из внешнего файла.

Я подозреваю, что многие другие значения элементов массива также устанавливаются программой в произвольные числа в середине выполнения (вероятно, около нуля) - хотя это невозможно явно проверить, учитывая большой размер массива - но по сути это что вызывает сбой программы (эти значения используются в других частях программы).

Итак, главный вопрос: почему значения элементов массива произвольно сбрасываются программой, хотя эти массивы СОХРАНЯЮТСЯ в ПЕРЕМЕННЫХ МОДУЛЯ, а этот модуль ИСПОЛЬЗУЕТСЯ всеми соответствующими подпрограммами? Разве оператор SAVE не предназначен для сохранения значений массивов?

Примечание. Я пытался запустить этот код на [a] ноутбуке Dell с Ubuntu Linux с 16 ГБ ОЗУ (никаких других программ, требующих памяти), и [b] на ноутбуке с Windows с 16 ГБ ОЗУ. На обеих этих машинах я использовал компилятор gfortran, и возникла та же проблема. При небольших значениях N код работает нормально — проблема возникает только с очень большими массивами.

Файл vars.f90

MODULE VARIABLES

   ......
   ......
   INTEGER :: N
   REAL(8), DIMENSION(:), ALLOCATABLE :: X,Y,Z

   SAVE N
   SAVE X,Y,Z
   .....
   .....
   ... (several other variable declarations) .... 

END MODULE VARIABLES

Распределить файл.f90

MODULE ALLOCATE_ARRAYS

CONTAINS

SUBROUTINE ALLOC_ARR
   USE VARIABLES
   USE DEALLOCATE_ARRAYS

   .....

   ALLOCATE(X(N),Y(N),Z(N), STAT=IALLOC)
   IF (IALLOC /= 0) THEN
      WRITE(6,*)' ERROR while allocating arrays X Y Z'
      CALL DEALLOC_ARRAYS
      STOP
   ENDIF

   .....
END SUBROUTINE ALLOC_ARR

END MODULE ALLOCATE_ARRAYS

Файл освободить.f90

MODULE DEALLOCATE_ARRAYS

CONTAINS

SUBROUTINE DEALLOC_ARR
   USE VARIABLES
   .....

   IF (ALLOCATED(X)) DEALLOCATE(X)
   IF (ALLOCATED(Y)) DEALLOCATE(Y)
   IF (ALLOCATED(X)) DEALLOCATE(X)

   ...
END SUBROUTINE DEALLOC_ARR

END MODULE DEALLOCATE_ARRAYS

Файл main.f90

PROGRAM PROG1

   USE VARIABLES
   USE ALLOCATE_ARRAYS
   USE DEALLOCATE_ARRAYS

   .....

   N = 18666800      ![in practice this is read from an external input file]
   CALL ALLOC_ARR    !Allocate X,Y,Z each to be of length N

   !Read all values of X(1:N), Y(1:N) and Z(1:N) from an external file] ...
   OPEN(11,FILE=datafile.txt,STATUS='OLD')

   DO I = 1,N
      READ(11,*,IOSTAT=ISTAT) X,Y,Z
         IF ( ISTAT /= 0 ) THEN
            WRITE(6,*)'   *** SERIOUS WARNING ***'
            WRITE(6,*)'   Something went wrong while trying to read numbers X Y Z at I = ',I
            READ(5,*)
         ENDIF
   ENDDO

   CLOSE(11)

   CALL SUB1

   .....
   CALL DEALLOC_ARRAYS

END PROGRAM PROG1


SUBROUTINE SUB1

   USE VARIABLES
   USE ALLOCATE_ARRAYS
   USE DEALLOCATE_ARRAYS

   .....

   DO I = 1,N
      write(6,*)' X(608673)  Y(608673)  Z(608673) = ',X(608673),Y(608673),Z(608673)  ![this statement only inserted for tracing the bug]
      CALL SUB2(I,X(I),Y(I),Z(I))
   ENDDO

END SUBROUTINE SUB1


SUBROUTINE SUB2(IPASS,XPASS,YPASS,ZPASS)

   USE VARIABLES
   USE ALLOCATE_ARRAYS
   USE DEALLOCATE_ARRAYS

   [carry out some calculations that use the values of IPASS, XPASS, YPASS, ZPASS, but never change their values]

END SUBROUTINE SUB2

Файл datafile.txt (небольшой образец):

.....
.....
10879.544935    1200.249974       28.290163
10914.193168     205.374638      236.847393
23872.837623    3634.498293    23721.923293
.....
.....

person Curious Leo    schedule 11.09.2019    source источник
comment
Я часто получаю ошибку сегментации со (старыми) программами на Фортране и большими размерами, потому что где-то всегда есть массив, выделенный стеком. В таких случаях помогает использование ulimit -s unlimited для обхода ограничений на размер стека (в Linux). Кроме того, запуск вашей программы с помощью Valgrind может указать вам на что-то.   -  person jacob    schedule 11.09.2019
comment
@jacob спасибо, но, к сожалению, это не помогло.   -  person Curious Leo    schedule 12.09.2019


Ответы (1)


Если большие числа переполняются, то может быть что-то вроде следующего:

USE ISO_C_BINDING
INTEGER(KIND=C_Int32_t) :: N

Или начните с добавления некоторых операторов отладки, чтобы убедиться, что числа, которые вы передаете, действительно получены, как и ожидалось.

Если 32 бита недостаточно, то C_Int64_t может содержать большие числа.

REAL(8) также может быть REAL(KIND=C_DOUBLE) с использованием ISO_C_BINDING, а ISO_C_BINDING — неплохой подход к определению размеров.

person Holmz    schedule 11.09.2019
comment
Спасибо. Я пробовал оба, C_Int32_t и C_Int64_t, но, к сожалению, ни один из них не решил проблему. - person Curious Leo; 12.09.2019