如何拥有可变等级(维数)的可分配数组?
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
决定一切。
但我对以下陈述和说明有疑问:
- 对于
dimension
,我没有相同数量的维度(3 或 4)
- 对于
allocate
,参数的数量是可变的(3或4)
- 对于初始化
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
希望这对您有所帮助,
费德里科
我有两个程序: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
决定一切。
但我对以下陈述和说明有疑问:
- 对于
dimension
,我没有相同数量的维度(3 或 4) - 对于
allocate
,参数的数量是可变的(3或4) - 对于初始化
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
希望这对您有所帮助, 费德里科