【问题标题】:how to initialize a large array in Fortran?如何在 Fortran 中初始化一个大数组?
【发布时间】:2016-02-15 15:04:36
【问题描述】:

我有一个 Fortran 函数,我想在编译时初始化一个大数组。下面是一个简化的工作示例,其中fill_coefficients 中的参数coeff 的大小已大大减小。

coeff 很大时如何编写类似的代码,而不超过maximum of 255 continuation linesmaximum of 132 characters per line?这里的fill_coefficients 应该真的是PURE,这可能导致无法在运行时从文件中读取一次coeff,然后存储结果。

文件“main.f03”:

    PROGRAM main
        USE coefficients
        IMPLICIT NONE

        REAL(dp), ALLOCATABLE, DIMENSION(:,:) :: matrix

        CALL fill_coefficients(matrix,2)

        PRINT *, "The first row of 'matrix':"
        PRINT *, matrix(1,:)
    END PROGRAM main

文件“coefficients.f03”:

    MODULE coefficients
        USE iso_fortran_env
        IMPLICIT NONE

        INTEGER, PARAMETER :: dp = REAL64

    CONTAINS
        PURE SUBROUTINE fill_coefficients(my_coefficients, n)
            IMPLICIT NONE
            REAL(dp), ALLOCATABLE, DIMENSION(:,:), INTENT(OUT) :: my_coefficients
            INTEGER, INTENT(IN) :: n

            ! The size of the following array would be roughly 200 x 200 = 40.000.
            REAL(dp), DIMENSION(3,3), PARAMETER :: coeff = &
                RESHAPE ( &
                [ + 10.6770782520313112108115239655957106_dp, &
                - 854.166260162504896864921917247656850_dp, &
                - 85.4166260162504896864921917247656850_dp, &
                + 16250.5130995916556628551394756366716_dp, &
                + 6747.91345528378868523288314625648912_dp, &
                + 106.770782520313112108115239655957106_dp, &
                - 123256.191341449456617608232658836883_dp, &
                - 8328.12103658442274443298869316465429_dp, &
                + 500381.272281447399894682070647642979_dp ], &
                [3,3] )

            IF (ALLOCATED(my_coefficients)) DEALLOCATE(my_coefficients)
            ALLOCATE(my_coefficients(n,n))

            my_coefficients = coeff(1:n,1:n)
        END SUBROUTINE fill_coefficients
    END MODULE coefficients

输出:

The first row of 'matrix': 10.677078252031311 16250.513099591655

【问题讨论】:

  • 如果它那么大,你不能从文件中读取内容吗?你需要parameter吗?
  • 我找不到我认为存在的答案/问题,但您也可以看到stackoverflow.com/q/25811884。您可以通过与链接答案类似的方式从其他较小的命名常量有效地构建命名常量。
  • 对@VladimirF 的评论的进一步说明:你说“应该很纯粹”。你真的需要它是纯粹的(这使我的第一个建议重复无效)还是你也在问这个要求?也就是,我们能不能根据不纯来提出建议?
  • 我希望fill_coefficients 是 PURE,因为从现在起大约一年后,我可能需要运行我的 FEM 多核,并且该例程位于最内层循环中。我相信这使得在运行时无法从文件中读取一次coeff,然后存储结果。我也许可以使用从其他几个较小的数组构建数组的“技巧”,或者分多个步骤进行。除了该解决方案的丑陋之外,我最终不会多次存储所有元素吗?
  • 关于您的预期用途和pure 的要求:我怀疑您可能想要考虑不涉及内部循环中的巨大数组分配的方法。也许可以从文件中读取它,然后只对数组部分执行操作(例如我们在 F77 时代如何使用静态内存布局进行操作)。 [但那是题外话:如果你真的想要求 pure 我不会争论 - 有时这就是想要的。]

标签: arrays fortran compile-time-constant


【解决方案1】:

从维护的角度来看(也许正如 cmets 中所建议的那样),我会将数据读入 单独的非纯子例程中的模块变量,该子例程在程序启动时调用一次。 fill_coefficients 然后成为该模块变量的简单赋值,并且仍然可以是 PURE。

MODULE coefficients
  IMPLICIT NONE
  ...
  ! Could be PUBLIC, PROTECTED, then you could directly 
  ! assign from it and dispense with fill_coefficients 
  ! altogether.
  REAL(dp), PRIVATE :: coeff(200,200)
CONTAINS
  SUBROUTINE init
    INTEGER :: unit 
    OPEN( NEWUNIT=unit,  &
          FILE='lots-of-numbers.bin',  &
          FORM='UNFORMATTED',  &
!         ACCESS='STREAM',  &    ! Maybe - depending on how you write it.
          STATUS='OLD' )
    READ (unit) coeff
    CLOSE(unit)
  END SUBROUTINE init

  PURE SUBROUTINE fill_coefficients(my_coefficients, n)
    ! implicit none already in force due to the statement in 
    ! the specification part of the host module.
    ! IMPLICIT NONE    
    REAL(dp), ALLOCATABLE, DIMENSION(:,:), INTENT(OUT) :: my_coefficients
    INTEGER, INTENT(IN) :: n

    ! This test is redundant - my_coefficients is INTENT(OUT) so 
    ! it must be not allocated at this point.
    ! IF (ALLOCATED(my_coefficients)) DEALLOCATE(my_coefficients)

    ! This allocate statement is redundant - allocation will 
    ! happen automatically under F2003 with the assignment.
    ! ALLOCATE(my_coefficients(n,n))

    my_coefficients = coeff(1:n,1:n)
  END SUBROUTINE fill_coefficients
END MODULE coefficients

如果您必须将coeff 作为编译时参数,则将其组装成源代码可管理的块——也许是逐列。每个声明的限制是行长 (132) 和续行数 (255)。

REAL(dp), PARAMETER :: column_1(200) = [  &
     + 10.6770782520313112108115239655957106_dp, &
     - 854.166260162504896864921917247656850_dp, &
     - 85.4166260162504896864921917247656850_dp, &
     ... ]
REAL(dp), PARAMETER :: column_2(200) = [ ... ]
...
REAL(dp), PARAMETER :: column_200(200) = [ ... ]

REAL(dp), PARAMETER :: coeff(200,200) = RESHAPE( [  &
       column_1, column_2, ..., column_200 ],  &
     SHAPE=[200,200] )

用 PARAMETER 声明的东西被命名为常量。从概念上讲,这些仅在编译时存在 - 根据您对命名常量所做的操作,编译器可能会或可能不会在可执行映像中为常量留出存储空间。

大的命名常量可能会导致编译器在编译文件时出现问题。

【讨论】:

  • 我正在自动生成要在任何计算机上使用的 Fortran 代码,这让我对FORM='UNFORMATTED' 的怀疑程度比我通常的情况还要高。但这当然是正确的想法,尽管以这种方式从 Python 自动生成整个 Fortran 代码有点棘手。
  • 您可以根据需要以人类可读文本的形式写入和读取文件,IO 操作只需要更长的时间,但在 Fortran 方面,每个图像只需要发生一次.
  • 作为记录,我将提到我的最终解决方案,在自动生成代码时实现起来更简单一些。而不是从文件中读取,我只是这样做 SUBROUTINE init() coeff(1,1)=1.0_dp coeff(2,1)=2.0_dp .... coeff(199,200)=0.0_dp coeff(200,200)=-43.7_dp END SUBROUTINE init 程序稍微臃肿的大小对我来说没有问题,模块作为一个整体很简单,即使开头有这么长的列表。感谢大家的帮助!
猜你喜欢
  • 2011-04-12
  • 2012-05-20
  • 2013-03-02
  • 2012-08-03
  • 2017-05-17
  • 2010-12-28
相关资源
最近更新 更多