我过去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(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意味着当我们解决较少的方程时求解器变慢——未使用的内存位置将减少内存带宽。
我想做的是利用具有可分配属性的派生类型。这样,我可以仅使用运行时定义的所需方程数(即数组_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'在编译时不可知。
第二次编辑
有人建议我提供带有参数化派生类型的工作和非工作代码示例。
工作示例:
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
是一个 长度 参数化类型(在此注释中未显示)。这种长度参数化类型似乎适用于这里。 - francescalussequence
语句。 - High Performance Mark