【问题标题】:Share allocatable Arrays共享可分配数组
【发布时间】:2013-08-23 12:23:29
【问题描述】:

我有一些可分配的数组,需要在一些子程序之间共享。我通常会将它们作为参数传递,或者将所有内容都写在一个模块中,但恐怕这在我的情况下是不可能的。

我只编写一些自己的子程序,并使用 FEM-Solver 提供和描述的子程序。所以我不能改变这个子程序的参数或将它们包装在一个模块中。

据我所知,在编译时也无法构建具有未知大小数组的公共块。

还有其他东西可以传递我的数组吗?

更新
目前我的程序环境是这样的:

我有一个由 FEM 程序提供的子程序,它在每次增量后被调用,这会调用我的几个子程序,在这些子程序中我为每个节点或其中的一个子集计算一些值。

为了在后期模拟中显示这些值,我必须将它们传递给另一个子程序。该子程序由 FEM 求解器在增量结束时为每个节点调用。所以将我的代码转移到这个子程序会产生很多开销。

我的想法是计算一次值,将值存储在一个数组中,然后将该数组传递给第二个子程序,在那里它们将被写入计算的数据库。

更新
一些伪代码:
从程序行为假设:

 Program FEM-solver
     *magic* 
     call ENDINC(ar1,ar2)
     *something* 
     do NodeID=1,Sum_Of_Nodes
        do valueID=1,Sum_Of_User_Computed_Values !(defined in preprocessing)
           call nodeval(NodeID,valueID,Value,ar3,...,arN)
        end do
     end do
     *voodoo* 
 end program FEM-solver    

书面和工作:

Subroutine ENDINC(ar1,ar2)
  *Computation of some node values*
  *Calling of own Subroutines, which compute more values*
  *Writing an array with results values for some/each node(s)*
   nodersltArr(NodeID,rslt)=*some Value*
end Subroutine ENDINC

需要,将计算的值写入节点解决方案数据库:

Subroutine nodeval(NodeID,valueID,Value,ar3,...,arN)  
  *called for each NodeID and valueID*
   value=noderslArr(NodeID,valueID)
end subroutine nodeval

【问题讨论】:

  • 你能用pointers吗?
  • 我想我可以使用指针,但我对它们没有经验,也不知道如何将它们链接到子例程之外的数组。你能给我更多的细节/一个例子吗?
  • FEM-Programm提供的一些子程序和公共块使用'Cray指针',所以我想我也可以使用指针。
  • 你的描述太让我困惑了。也许是一些显示子程序连接和信息流的伪代码?您的一个子例程可以将数据存储在模块数组中,以供您的另一个子例程稍后使用。
  • 如果您在程序开始时知道结果值数组的大小,则将该数组作为可分配数组放在模块中。在主程序中分配数组。循环通过您的 FEM 求解器。在计算每个值时,将其作为数组存储在您的一个子例程中。然后当整个计算完成后,你自己的代码的另一部分可以处理结果。您不想修改的 FEM 代码不需要“了解”模块及其阵列。也许你需要一个用户定义的类型而不是一个简单的数组。

标签: arrays fortran


【解决方案1】:

您可以将可分配数组传递给未声明为使用可分配数组的过程,只要该数组在调用之前分配。 (当然,在没有该属性的情况下,您不能在声明它的过程中将该数组用作可分配数组。)也许这会解决您的问题。在您编写的代码中分配数组,然后将其作为参数传递给 FEM 求解器。

示例代码:(我通常会将函数放入模块中,但你说不能这样做,所以我写了一个示例来展示不使用模块的情况。)

function MySum ( RegArray )

real :: MySum
real, dimension (:), intent (in) :: RegArray

MySum = sum (RegArray)

end function MySum


program TestArray

   implicit none

   interface AFunc

      function MySum ( SomeArray )

         real :: MySum
         real, dimension (:), intent (in) :: SomeArray

      end function MySum

   end interface AFunc

   real, dimension (:), allocatable :: AllocArray
   integer :: N
   real :: answer

   write (*, '("Input array size: ")', advance="no")
   read (*, *) N

   allocate ( AllocArray (1:N) )
   AllocArray = 1.0

   answer = MySum ( AllocArray )
   write (*, *) answer

end program TestArray

--------- 编辑:第二个概念 ---------

在两个子例程之间共享一个可分配数组,而调用例程并不“知道”该数组。

module MySubs

   real, allocatable, dimension (:,:) :: array

contains


subroutine One ( x, y, ... N, M )

   integer, intent (in) :: N, M

   if ( .NOT. allocated (array) ) allocate ( array (N, M) )


end subroutine One


subroutine Two ( .... )


end subroutine Two


end module MySubs

更新:注意:这种方法可用于在两个例程之间传递信息,而无需主程序访问模块......对于问题,无需修改原始主程序。该示例的一部分是如何分配数组:该示例通过让首先使用数组的子例程测试数组是否已分配来实现这一点——如果没有,它会分配数组。

【讨论】:

  • 我了解,您将分配的数组作为参数传递给子程序,但正如我所写,我必须将它传递给 FEM 包提供的子程序,在该子程序中我无法修改参数。
  • 将其作为参数、公共变量或模块变量传递。但是如果你不能改变被调用的函数,如果它还没有提供这样做的方法,你将如何告诉它如何使用你的数组?
  • 我可以编辑调用的子例程,但不能编辑传递的参数。 common 的问题是,我不知道编译时数组的大小。我可以使用模块,但我必须将模块包含到子程序中,并且不能在子程序中使用“cotains”,因为我无法将模块包含到主程序中。
  • 结果不是用子程序编写的,而是通过调用子程序主程序查询每个值要存储的值。
  • 您仍然可以在模块中使用可分配数组在两个子例程之间传递信息,而无需主程序知道它。让首先使用数组的子程序测试数组是否被分配——如果没有,分配它。如果子程序无法知道数组的大小,那么您可能需要不同的设计。例如,将解决方案记录在链表中。
【解决方案2】:

以下三个示例都适用于 gfortran。第二个可能在某些编译器上失败,因为它使用 F2003 功能(可分配的虚拟参数)和not all compilers are 100% F2003 compliant。但是,大多数实现ISO TR 15581(包括此功能)。

第一版,可以使用通用指针来分配数组。

program hip
   implicit none
   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   double precision, allocatable, dimension(:, :), target :: a
   allocate(a(100, 100))
   a(1, 1) = 3.1416d0
   p => a
   call hop
   deallocate(a)
end program

subroutine hop
   implicit none
   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   print *, size(p, 1), size(p, 2), p(1, 1)
end subroutine

第二个版本,在一个子程序中分配然后调用另一个。还是需要在主程序中声明数组。

program hip
   implicit none

   interface
      subroutine hip_alloc(arr)
         double precision, allocatable, dimension(:, :) :: arr
      end subroutine
   end interface

   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   double precision, allocatable, dimension(:, :) :: a
   p => null()
   print *, "a:", allocated(a)
   print *, "p:", associated(p)
   call hip_alloc(a)
   print *, "a:", allocated(a)
   print *, "p:", associated(p)
   call hop
   deallocate(a)
end program

subroutine hip_alloc(arr)
   implicit none
   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   double precision, allocatable, dimension(:, :), target :: arr
   allocate(arr(100, 100))
   arr(1, 1) = 3.1416d0
   p => arr
end subroutine

subroutine hop
   implicit none
   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   print *, size(p, 1), size(p, 2), p(1, 1)
end subroutine

第三版,这里我们先调用一个返回指针的函数,然后把这个指针通过一个common传递给一个子程序。该函数执行分配,如第二个示例所示。指针在主程序中被释放,但可能在其他地方。

program hip
   implicit none

   interface
      function hip_alloc(n)
         integer :: n
         double precision, dimension(:, :), pointer :: hip_alloc
      end function
   end interface

   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   p => null()
   print *, "p:", associated(p)
   p => hip_alloc(100)
   print *, "p:", associated(p)
   call hop
   deallocate(p)
end program

function hip_alloc(n)
   implicit none
   integer :: n
   double precision, dimension(:, :), pointer :: hip_alloc
   allocate(hip_alloc(n, n))
   hip_alloc(1, 1) = 3.1416d0
end function

subroutine hop
   implicit none
   double precision, dimension(:, :), pointer :: p
   common /hiphop/ p
   print *, size(p, 1), size(p, 2), p(1, 1)
end subroutine

【讨论】:

  • 当 hip 是一个子程序并且 hop 会在同一个程序的 hip 之后被调用时,这是否也有效?
  • 是的,见上文,我添加了一个示例。不过,您仍然需要在封闭程序中声明数组。还需要一个接口块。顺便说一句,你不需要在主程序中声明公共变量,我把它放在这里是为了显示 hip_alloc 调用前后的内容
  • 添加了一个带有执行分配功能并返回指针的版本。
  • 还有另一条评论,主要针对版本 2:如果您解除分配数组,则指针仍被定义,但指向未定义的区域。您必须小心这一点,并在数组被释放后立即取消您的指针,当然以后不要使用它。
【解决方案3】:

我不明白为什么写MODULE 行不通,但是您考虑过CONTAINS 吗? CONTAINS 声明之上的所有内容对CONTAINS 之下的子例程都是可见的:

PROGRAM call_both
   INTEGER,DIMENSION(2) :: a, b
   a = 1
   b = 2
   PRINT *,"main sees", a, b
   CALL subA
   CALL subB
 CONTAINS
   SUBROUTINE subA
      PRINT *,"subA sees",a,b
   END SUBROUTINE subA

   SUBROUTINE subB
      PRINT *,"subB sees",a,b
   END SUBROUTINE subB
END PROGRAM call_both

输出将是

main sees           1           1           2           2
subA sees           1           1           2           2
subB sees           1           1           2           2

这也适用于 ALLOCATABLE 数组。

【讨论】:

  • 嗨,我已经更新了这个问题。问题是,我有两个由 FEM 程序提供和调用的子程序,我必须在它们之间传递数组,因此将它们放在一个模块中需要我更改调用它们的求解器的代码,而我不能.您的解决方案似乎朝着同一个方向发展。
  • @Alex:我想我只是不明白为什么你无权访问你的代码。
  • @Kyle Kanos:显然 Alex 有一个商业编写的程序,它将调用用户编写的子程序。在无法访问商业程序的源代码的情况下,用户提供的子例程必须具有预定义的参数列表。 Alex 想要以预先确定的参数列表不允许的方式传递信息。至少这是我对这个问题的解释。我不明白为什么他不能通过他的子程序知道的模块而不是主程序来做到这一点。
  • @M.S.B.:啊哈,这确实把事情弄清楚了。我应该认为一个模块也可以工作。
  • @M.S.B.:是的,这是我的实际问题。到目前为止,我只得到了将 Subroutines 放入一个 Module 并在 Subroutines 之间共享数组的技巧。但是这样主程序就不会知道子程序在哪里,因为它不知道它必须包含模块。我将在另一条评论中尝试您的建议,方法是将一个模块(其中只有可分配数组)嵌入到两个子例程中。
猜你喜欢
  • 2015-03-29
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-03
相关资源
最近更新 更多