存储要沿所有维度操作的多维数组

store multi dimensional arrays to be operated on along all the dimensions

前言

我正在编写的 Fortran 程序应该根据 ndims 处理 1D、2D 和 3D 问题,它可以是 1、2 或 3,并且是从输入文件中读取的。

在这些情况下,感兴趣的 quantity/ies 可以存储在数组中(一个可以命名为 phi

  1. 排名 dimsALLOCATABLE(:)ALLOCATABLE(:,:)ALLOCATABLE(:,:,:)),
  2. 或在秩为 3 的数组中(ALLOCATABLE(:,:,:) 二维中第三维设置为 1 或一维中第二维和第三维均设置为 1);

这两种情况在 this answer 中都有很好的解释。第一种方法对我来说似乎更优雅,但在下文中我假设第二种方法,这绝对更简单。

这些量必须由几个子程序(例如mysub)沿着ndims维度(沿着"pencils"应该给出一个图形的想法)进行操作,所以我应该调用一些东西喜欢

SELECT CASE (ndims)

! 3D case
CASE (3)
  DO j = ...
    DO k = ...
      CALL mysub(phi(:,j,k))
    END DO
  END DO
  DO i = ...
    DO k = ...
      CALL mysub(phi(i,:,k))
    END DO
  END DO
  DO i = ...
    DO j = ...
      CALL mysub(phi(i,j,:))
    END DO
  END DO

! 2D case
CASE (2)
  DO j = ...
    DO k = ...
      CALL mysub(phi(:,j,1))
    END DO
  END DO
  DO i = ...
    DO k = ...
      CALL mysub(phi(i,:,1))
    END DO
  END DO

! 1D case
CASE (1)
  DO j = ...
    DO k = ...
      CALL mysub(phi(:,1,1))
    END DO
  END DO
END SELECT

真题

任何人都可以建议我(或帮助我设计!)一种不同的存储方式 phi(可能涉及派生数据类型?)以便我可以按如下方式折叠前面的代码?

DO id = 1, ndims
  CALL mysub2(phi,id)
END DO

(这里mysub2作用于mysub的位置。)

所以问题是我应该如何存储 phi,以便我可以用第二个代码替换第一个代码?

也许我可以 return 前言并决定遵循第 1 点,在这种情况下编写通用接口会更容易。我认为,这只是 "hide" 正是 SELECT CASE 会做的事情的一种方式。这两个 (SELECT CASE/generic INTERFACE) 中哪个更有效?

这是解决这个问题的唯一两种方法吗?

您可能想要这样的东西:

program test

   integer :: j,ndims
   integer :: n ! rank of each dimension, could also be read from input an allocated separately

   type arr
      real(8) :: x(n) ! one array for each dimension
   end type

   type(arr),allocatable :: phi

   read(*,*) ndims
   allocate(phi(ndims))

   do j=1,ndims
      call mysub(phi(j)%x) ! acts on the array in dimension j
   end do

contains

   subroutine mysub(x)
   ...
   end subroutine

end program

也许我理解错了,但我认为具体问题的答案是根本不对phi的存储或声明进行任何更改。

在原始代码中,三维数据(将数据的秩与用于存储数据的数组的秩区分开来)沿第一维切片处理,然后是第二维,然后是第三维。二维数据按第一个处理,然后是第二个,一维数据只按第一个处理。

所以随着 id 从 1 到数据中的维数,考虑以下 mysub2 的实现:

SUBROUTINE mysub2(phi, id)
  TYPE(pink_elephant), INTENT(IN) :: phi(:,:,:)
  INTEGER, INTENT(IN) :: id

  INTEGER :: i, j, k

  SELECT CASE (id)
  CASE (1)
    DO j = ...
      DO k = ...
        CALL mysub(phi(:,j,k))
      END DO
    END DO

  CASE (2)
    DO i = ...
      DO k = ...
        CALL mysub(phi(i,:,k))
      END DO
    END DO

  CASE (3)
    DO i = ...
      DO j = ...
        CALL mysub(phi(i,j,:))
      END DO
    END DO

  END SELECT
END SUBROUTINE mysub2

~~

通用接口总是可以在 "compile time" 处解析 - 将由特定 CALL 语句或函数引用调用的特定过程(非类型绑定)或绑定(类型绑定)可以通过查看来确定在代码中的声明处。

如果您遇到 "runtime" 信息会影响过程选择的情况,那么必须有一些其他的可执行机制,除了泛型解析之外或附加到泛型解析中播放 - if 语句、select 案例、动态调度等

因此,询问通用解决方案是否比可执行决策更有效并没有特别的意义——它们是不同的东西。