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

问题描述 投票:0回答:1

因此,我一直在搜索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的事实使求解器变慢-未使用的内存位置将减少内存带宽-。

我想做的是利用具有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))

不知道要分配什么,所以它分配了大块内存(以适应以后将要分配的可分配类型),或者只是将索引array(1)分配给array(elements)而没有其他分配(这意味着结构的实际内容稍后会在循环中存储。

如何使第二种方法像第一种方法一样存储变量?

EDIT#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'不知道。

EDIT#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

编译器(错误)错误:

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)

我应该以其他方式声明/分配数组吗?

因此,我一直在搜索3-4天,但找不到该问题的答案。它与特定派生类型的可分配数组有关。这都是...的全部部分]]

麻烦的行是

type(array_t(equations)), allocatable, save :: flowsol(:)

这里,您希望将长度参数化的类型保存为对象。作为编译器对象,保存的对象不能是自动对象。

现在,为什么flowsol是自动对象?它是自动的,因为其类型为array_t(equations)equations不是常数。相反,您希望推迟type参数(与数组的形状一样):

type(array_t(:)), allocatable, save :: flowsol(:)

当您要分配这样的对象时,您需要提供长度类型参数值:

allocate(array_t(equations) :: flowsol(elements))
struct types fortran memory-alignment
1个回答
0
投票

麻烦的行是

© www.soinside.com 2019 - 2024. All rights reserved.