如何拥有可变等级(维数)的可分配数组?

How to have an allocatable array of variable rank (number of dimensions)?

我有两个程序:2D 和 3D 版本。 例如,这是一个简化的 2D 版本:

program main

    integer, parameter :: nDim = 2
    integer :: size
    real, dimension(:,:,:), allocatable :: myArray

    size=4 ! Normally read from a file
    allocate(myArray(1:size,1:size,1:nDim))
    call initArray(myArray)

contains
    
    subroutine initArray(myArray)
    
        real, dimension(1:size,1:size,1:nDim) :: myArray
    
        myArray(1:size,1:size,1:nDim) = 5.d0
     
        return  
    end subroutine initArray
    
end program main

还有3D版,

program main

    integer, parameter :: nDim = 3
    integer :: size
    real, dimension(:,:,:,:), allocatable :: myArray

    size=4 ! Normally read from a file
    allocate(myArray(1:size,1:size,1:size,1:nDim))
    call initArray(myArray)

contains
    
    subroutine initArray(myArray)
    
        real, dimension(1:size,1:size,1:size,1:nDim) :: myArray
    
        myArray(1:size,1:size,1:size,1:nDim) = 5.d0
     
        return  
    end subroutine initArray
    
end program main

这些程序非常相似,我希望只有一个程序,参数 nDim 决定一切。

但我对以下陈述和说明有疑问:

  1. 对于dimension,我没有相同数量的维度(3 或 4)
  2. 对于allocate,参数的数量是可变的(3或4)
  3. 对于初始化myArray,维数是可变的(3 或 4)

是否有纯 Fortran 的解决方案,还是我应该使用 C 预处理器?

感谢您的回答。

我知道,它很丑但它能用而且用起来很方便

#define nDim 3

#if nDim==2
#  define xdimCpp :,:
#  define xboundsCpp(X) 1:X,1:X
#elif nDim = 3
#  define xdimCpp :,:,:
#  define xboundsCpp(X) 1:X,1:X,1:X
#endif
  
program main

    integer :: size
    real, dimension(xdimCpp,:), allocatable :: myArray

    size=4 ! Normally read from a file
    allocate(myArray(xboundsCpp(size),1:nDim))
    call initArray(myArray)
    print *,myArray
    
contains
    
    subroutine initArray(myArray)
    
        real, dimension(xboundsCpp(size),1:nDim) :: myArray
    
        myArray(xboundsCpp(size),1:nDim) = 5.d0
     
        return  
    end subroutine initArray
    
end program main

这是一个典型的例子,object-oriented 编程有帮助。 您有两个程序响应相同的 API,但内部计算将不同(2D 或 3D)。 您希望 2d 和 3d 网格都扩展通用的“网格类型”

module grids
   implicit none

   type, abstract, public :: grid
      contains
      procedure (grid_init), deferred :: init
   end type grid

   type, public, extends(grid) :: grid2D
      real, allocatable :: myArray(:,:)
      contains
         procedure :: init=>init_2d
   end type grid2D
   
   type, public, extends(grid) :: grid3D
      real, allocatable :: myArray(:,:,:)
      contains
         procedure :: init=>init_3d
   end type grid3D

   ! Define procedure APIs 
   abstract interface
      subroutine grid_init(this,size)
         import grid
         class(grid), intent(inout) :: this
         integer, intent(in) :: size
      end subroutine grid_init
   end interface

   contains

   ! Define ACTUAL procedures for the 2d and 3d grids
   subroutine init_3d(this, size)
      class(grid3D), intent(inout) :: this
      integer, intent(in) :: size
      allocate(this%myArray(size, size,size))
   end subroutine init_3d
   subroutine init_2d(this, size)
      class(grid2D), intent(inout) :: this
      integer, intent(in) :: size
      allocate(this%myArray(size,size))
   end subroutine init_2d
end module grids

具体实现由你决定,但重点是:

  • 通过多态API公开所有常见的“操作”(例如在网格上);在 abstract class 中定义它们,因此编译器将检查您在子 classes 中所做的一切是否正确;

  • 在派生类型中隐藏所有 dimension-dependent 代码和数据;

  • 理想情况下,您会尝试以 dimension-independent 方式定义 public 接口的所有例程。这通常是不可能的,在这种情况下,您将在无法共享呼叫的地方恢复使用 type-checking。

例如,您可能需要 2 或 3 个输入参数:您可以这样做

class(grid), allocatable :: myGrid

select type (myGrid)
   type is (grid2D); [...]
   type is (grid3D); [...]
   class default; [...]
end select

一个简单的测试程序看起来像

program test_grids
   use grids
   implicit none

   type(grid2D) :: fixed_2d
   type(grid3D) :: fixed_3d
   class(grid), allocatable :: polymorphic

   ! Initialize non-polymorphic grids (fixed type)
   call fixed_2d%init(5)
   call fixed_3d%init(10)

   ! Initialize polymorphic grid as 2D
   allocate(grid2D :: polymorphic)
   call polymorphic%init(10)

   ! Initialize polymorphic grid as 3D
   deallocate(polymorphic)
   allocate(grid3D :: polymorphic)
   call polymorphic%init(10)

end program test_grids

希望这对您有所帮助, 费德里科