Struct Fortran-可分配派生类型的可分配数组

Struct Fortran-可分配派生类型的可分配数组,struct,types,fortran,memory-alignment,Struct,Types,Fortran,Memory Alignment,所以在过去的3-4天里,我一直在寻找这个问题的答案。 它与特定派生类型的可分配数组有关。这是计算流体动力学解算器的所有部分。然而,实际应用并不重要。让我提供一些(快速)编程上下文 假设我们有一个简单的模块,它定义了一个固定大小的派生类型,主程序分配了一个包含多种类型的数组: module types integer, parameter :: equations = 10 type array_t double precision :: variable(equat

所以在过去的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(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)
...
...
在本例中,我们将参数方程设置为=10。在解算器中,我们始终将此大小保留为最大值(10):所有派生类型都具有解算器可能需要的最大尺寸。不幸的是,这意味着我们可能会分配比实际需要更多的内存-一些模拟可能只需要5或6个方程,而不是10-。此外,当我们求解较少的方程时,派生类型维度保持固定在大小10的事实会使解算器变慢-未使用的内存位置将减少内存带宽

我想做的是利用具有allocatable属性的派生类型。这样,我可以只使用所需数量的方程(即数组的维数)分配结构,这些方程将在运行时(而不是编译时)定义,并根据模拟参数进行更改

请看以下代码段:

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

有人建议我提供使用参数化派生类型的工作和非工作代码的示例

工作示例:

非工作示例:

编译器(ifort)错误:


我应该以不同的方式声明/分配数组吗?

麻烦的一行是

type(array_t(equations)), allocatable, save :: flowsol(:)
在这里,您希望长度参数化类型成为保存的对象。作为编译器对象,保存的对象不能是自动对象。此外,在模块的作用域中不能有自动对象

现在,为什么
flowsol
是一个自动对象?它是自动的,因为它的类型是数组(方程式):
方程式
不是常数。相反,您希望延迟类型参数(与数组的形状一样):

分配此类对象时,需要提供长度类型参数值:

allocate(array_t(equations) :: flowsol(elements))

如您所见,当
方程
是一个(命名的)常数时,事情会更幸福。在这种情况下,不会声明自动对象。

您可能正在寻找Fortran的参数化派生类型。我没有时间写一个答案,但这里有一些Q和As,以及网上的大量其他资源。给定
type(array\u t(:)、allocatable::cell\u solution(:)
相应的分配语句
allocate(array\u t(5)::cell\u solution(100))
似乎合适。这里,
array\u t
是一种长度参数化类型(此注释中未显示)。这样的长度参数化类型似乎在这里适用。谢谢您的输入!你的建议是可以汇编的。但是,我必须在解算器中实现它,看看它是否正常工作。问题是,我在使用可分配类型方法时遇到了内存失调问题。我希望这种方法以正确的方式存储变量。如果您关心内存中派生类型的布局,您可能需要研究
sequence
语句。不幸的是,此选项也非常慢。乍一看,它似乎比可分配方法更慢,但是我没有时间比较这两种方法。我可以肯定的是,它肯定比最初的方法慢得多(固定方程=10作为参数的方法)。由于这个结构是一个相对较大的解算器的一部分,我认为我不可能发布一个显示我所指的减速的代码示例。在上下文中,我唯一改变的是我改变了“等式”变量int
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(equations)), allocatable, save :: flowsol(:)
type(array_t(:)), allocatable, save :: flowsol(:)
allocate(array_t(equations) :: flowsol(elements))