Така че търсих през последните 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)
...
...
В този пример задаваме параметъра equations=10. В решаващия инструмент винаги оставяме този размер максимален (10): всички производни типове имат максималното измерение, което може да изисква решаващият инструмент. За съжаление, това означава, че може да отделим повече памет, отколкото всъщност ни е необходима - някои симулации може да се нуждаят само от 5 или 6 уравнения вместо 10. В допълнение, фактът, че размерът на извлечения тип остава фиксиран на размер 10, прави решаването по-бавно, когато решаваме за по-малко уравнения - неизползваните места в паметта ще намалят честотната лента на паметта.
Това, което искам да направя, е да използвам производни типове, които имат атрибута allocable. По този начин мога да разпределя структурите, като използвам само необходимия брой уравнения (т.е. измерения на 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))
не знае какво да разпредели, така че или разпределя голяма част от паметта (за да пасне на разпределяемия тип, който ще дойде по-късно), или просто разпределя индексите array(1) към array(elements) и нищо друго (което означава, че действителният съдържанието на структурата се съхранява по-късно, вътре в цикъла).
Как мога да накарам втория подход да съхранява променливите като първия?
РЕДАКТИРАНЕ #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
е дължина-параметризиран тип (не е показано в този коментар). Такива параметризирани по дължина типове изглеждат приложими тук. - person francescalus   schedule 21.11.2019sequence
. - person High Performance Mark   schedule 21.11.2019