Итак, я искал последние 3-4 дня и не нашел ответа на эту проблему. Это связано с размещаемыми массивами определенного производного типа. Все это часть решателя вычислительной гидродинамики. Однако фактическое приложение не имеет значения. Позвольте мне представить (краткий) контекст программирования.
Допустим, у нас есть простой модуль, определяющий производный тип фиксированного размера, и основная программа, которая выделяет массив нескольких типов:
module types
integer, parameter :: equations = 10
type array_t
double precision :: variable(equations) ! variables to solve
double precision :: gradient(equations,3) ! gradient of variables in x,y,z direction
double precision :: limiter(equations) ! limiter of variables
end type
end module
program test
use types
implicit none
type(array_t), allocatable :: array(:)
integer :: elements
elements = 100
allocate(array(elements))
end program
Этот фрагмент кода, конечно, можно скомпилировать с использованием любого компилятора. Поскольку размер array_t фиксирован и известен во время компиляции, нам нужно только выделить структуру array в одной строке (определяя количество повторений array_t внутри структуры).
Что касается ячеек памяти, переменные будут храниться следующим образом:
array(1)%variable(1) ! element 1
array(1)%variable(2)
...
...
array(1)%gradient(1,1) ! the rest of this 2D array will be written column-major in fortran
array(1)%gradient(2,1)
array(1)%gradient(3,1)
...
...
array(1)%limiter(1)
array(1)%limiter(2)
...
...
array(2)%variable(1) ! element 2
array(2)%variable(2)
...
...
В этом примере мы устанавливаем параметр sizes = 10. В решающей программе мы всегда оставляем этот размер максимальным (10): все производные типы имеют максимальный размер, который может потребоваться решающей программе. К сожалению, это означает, что мы можем выделить больше памяти, чем нам на самом деле нужно - для некоторых симуляций может потребоваться всего 5 или 6 уравнений вместо 10 -. Кроме того, тот факт, что размерность производного типа остается фиксированной на размере 10, замедляет работу решателя, когда мы решаем меньшее количество уравнений - неиспользуемые ячейки памяти уменьшают пропускную способность памяти.
Я хочу использовать производные типы с атрибутом allocatable. Таким образом, я могу выделить структуры, используя только необходимое количество уравнений (то есть размеры array_t), которые будут определены во время выполнения (а не во время компиляции) и будут меняться в зависимости от параметров моделирования.
Взгляните на следующий фрагмент кода:
module types
integer, save:: equations
type array_t
double precision, allocatable :: variable(:) ! variables to solve
double precision, allocatable :: gradient(:,:) ! gradient
double precision, allocatable :: limiter(:) ! limiter of variables
end type
end module
program test
use types
implicit none
type(array_t), allocatable :: array(:)
integer :: i,elements
equations = 10
elements = 100
allocate(array(elements))
do i=1,elements
allocate(array(i)%variable(equations))
allocate(array(i)%gradient(equations,3))
allocate(array(i)%limiter(equations))
enddo
end program
Пока это единственный способ, которым мне удалось заставить его работать. Решатель запускается и сходится, что означает, что синтаксис не только компилируемый, но и эквивалентен использованию фиксированного размера.
Однако при таком подходе решающая программа работает значительно медленнее даже для того же количества уравнений.
Это означает, что имеется рассогласование памяти. Судя по измеренным значениям времени выполнения, кажется, что переменные не сохраняются таким же образом, как при использовании фиксированного размера.
Во втором подходе, как переменные хранятся в глобальной памяти? Я хочу добиться того же шаблона, что и при первом подходе. Я чувствую, что первая строка, которая выделяет структуру
allocate(array(elements))
не знает, что выделить, поэтому либо выделяет большой кусок памяти (чтобы соответствовать выделяемому типу, который появится позже), либо просто выделяет массив индексов (1) для массива (элементов) и ничего больше (что означает, что фактический содержимое структуры сохраняется позже, внутри цикла).
Как я могу заставить второй подход хранить переменные, как и первый подход?
ИЗМЕНИТЬ №1
Поскольку параметризованные производные типы получили некоторую поддержку, я подумал, что было бы полезно опубликовать некоторые дополнительные сведения.
Параметризованные производные типы будут работать в сценариях, где массив выделяется внутри основной программы (как в опубликованном мною образце кода).
Однако мой случай из «реального мира» больше похож на следующий:
(file_modules.f90)
module types
integer, parameter :: equations = 10
type array_t
double precision :: variable(equations) ! variables to solve
double precision :: gradient(equations,3) ! gradient pf variables
double precision :: limiter(equations) ! limiter of variables
end type
end module
module flow_solution
use types
type (array_t), allocatable, save :: cell_solution(:)
end module
(file_main.f90)
program test
use flow_solution
implicit none
integer :: elements
elements = 100
allocate(cell_solution(elements))
end program
Они (как и следовало ожидать) компилируются и связываются отдельно через Makefile. Если я использовал параметризованный производный тип, файл модуля не может быть скомпилирован, потому что размер n типа неизвестен во время компиляции.
ИЗМЕНИТЬ №2
Мне посоветовали предоставить примеры рабочего и неработающего кода с параметризованными производными типами.
Рабочий пример:
module types
integer, parameter :: equations=10
type array_t(n)
integer, len :: n
double precision :: variable(n) ! variables to solve
double precision :: gradient(n,n) ! gradient
double precision :: limiter(n) ! limiter of variables
end type
end module
module flow_solution
use types
type(array_t(equations)), allocatable, save :: flowsol(:)
end module
program test
use flow_solution
implicit none
integer :: i,elements
elements = 100
allocate(flowsol(elements))
end program
Нерабочий пример:
module types
integer, save :: equations
type array_t(n)
integer, len :: n
double precision :: variable(n) ! variables to solve
double precision :: gradient(n,n) ! gradient
double precision :: limiter(n) ! limiter of variables
end type
end module
module flow_solution
use types
type(array_t(equations)), allocatable, save :: flowsol(:)
end module
program test
use flow_solution
implicit none
integer :: i,elements
equations = 10
elements = 100
allocate(flowsol(elements))
end program
Ошибка компилятора (ifort):
test.f90(16): error #6754: An automatic object must not appear in a SAVE statement or be declared with the SAVE attribute. [FLOWSOL]
type(array_t(equations)), allocatable, save :: flowsol(:)
---------------------------------------------------^
test.f90(16): error #6841: An automatic object must not appear in the specification part of a module. [FLOWSOL]
type(array_t(equations)), allocatable, save :: flowsol(:)
---------------------------------------------------^
compilation aborted for test.f90 (code 1)
Следует ли объявлять / размещать массивы по-другому?
type(array_t(:)), allocatable :: cell_solution(:)
, соответствующий оператор распределенияallocate(array_t(5) :: cell_solution(100))
может показаться подходящим. Здесьarray_t
- это тип с параметром length (не показан в этом комментарии). Такие типы с параметризацией длины кажутся здесь применимыми. - person francescalus   schedule 21.11.2019sequence
. - person High Performance Mark   schedule 21.11.2019