Fortran:将任意 "structures"传递给模块子例程

标签 fortran gfortran

我正在尝试编写一个通用子例程以实现最小化。由于我想要一个通用的子例程,目标函数可以有不同的参数,不仅在名称上,而且在维度上。所以我需要一种方法来传递该参数结构(我使用结构这个词,因为我的想法是在Matlab中使用类似结构类型变量的东西)。 我设法使用派生数据类型,它工作得很好,但是当我在同一个程序中有两个不同的目标函数时,问题就出现了。这是示例代码:

在主程序main.f90中:

MODULE MYPAR
  IMPLICIT NONE
  TYPE PARPASS1    !! Parameter passing structure 1
     INTEGER       :: a
     REAL          :: X(2,2)
  END TYPE PARPASS1

  TYPE PARPASS2    !! Parameter passing structure 2
     REAL          :: b
     REAL          :: Y(3)
  END TYPE PARPASS2
END MODULE MYPAR

PROGRAM MAIN

  USE MYPAR
  USE MYLIB
  IMPLICIT NONE

  INTEGER        :: am
  REAL           :: bm,Xm(2,2),Ym(3),sol1,sol2
  TYPE(PARPASS1) :: PARAM1
  TYPE(PARPASS2) :: PARAM2

  am = 1
  bm = 3.5
  Xm(1,:) = [1.0, 2.0]
  Xm(2,:) = [0.5, 2.5]
  Ym(1:3) = [0.25,0.50,0.75]

  PARAM1%a = am
  PARAM1%X = Xm
  PARAM2%b = bm
  PARAM2%Y = Ym

  CALL MYSUB(sol1,OBJ1,PARAM1)
  CALL MYSUB(sol2,OBJ2,PARAM2)
  PRINT *,sol1
  PRINT *,sol2

CONTAINS

  SUBROUTINE OBJ1(sumval,PARAM)
    REAL,INTENT(OUT)          :: sumval
    TYPE(PARPASS1),INTENT(IN) :: PARAM
    INTEGER                   :: a
    REAL,ALLOCATABLE          :: X(:,:)
    a = PARAM%a
    X = PARAM%X
    sumval = a+X(1,1)+X(2,2)
  END SUBROUTINE OBJ1

  SUBROUTINE OBJ2(divval,PARAM)
    REAL,INTENT(OUT)          :: divval
    TYPE(PARPASS2),INTENT(IN) :: PARAM
    REAL                      :: b
    REAL,ALLOCATABLE          :: Y(:)
    b = PARAM%b
    Y = PARAM%Y
    divval = b / (Y(1)+Y(2))
  END SUBROUTINE OBJ2

END PROGRAM MAIN

还有一个名为 mylib.90 的模块

MODULE MYLIB

  USE MYPAR
  IMPLICIT NONE

CONTAINS

  SUBROUTINE MYSUB(sol,FN,PARAM)
    REAL,INTENT(OUT)           :: sol
    TYPE(PARPASS1), INTENT(IN) :: PARAM
    CALL FN(sol,PARAM)
    sol = 2*sol
  END SUBROUTINE MYSUB

END MODULE MYLIB

显然,如果我用 CALL MYSUB(sol2,OBJ2,PARAM2)PRINT *,sol2 注释这些行,我的代码就会顺利运行。这是我在拥有两个“目标函数”之前所拥有的,但现在当我拥有它们时,它不起作用,因为 MYSUB 中的派生类型变量 PARPASS1 不能是任意的。

有什么想法吗?

最佳答案

您可以使用接口(interface)并重载子例程 MYSUB:

MODULE MYLIB

  USE MYPAR
  IMPLICIT NONE

  interface MYSUB
    module procedure MYSUB_PARPASS1, MYSUB_PARPASS2
  end interface

CONTAINS

  SUBROUTINE MYSUB_PARPASS1(sol,FN,PARAM)
    REAL,INTENT(OUT)           :: sol
    TYPE(PARPASS1), INTENT(IN) :: PARAM
    CALL FN(sol,PARAM)
    sol = 2*sol
  END SUBROUTINE MYSUB_PARPASS1

  SUBROUTINE MYSUB_PARPASS2(sol,FN,PARAM)
    REAL,INTENT(OUT)           :: sol
    TYPE(PARPASS2), INTENT(IN) :: PARAM
    CALL FN(sol,PARAM)
    sol = 2*sol
  END SUBROUTINE MYSUB_PARPASS2   

END MODULE MYLIB

然后你可以使用MYSUB来调用它,它会根据PARAM的TYPE来区分功能

编辑:好的,这个怎么样:

MODULE MYPAR
  IMPLICIT NONE

  type, abstract :: PARPASS
   contains
     procedure(func), deferred :: OBJ
  end type PARPASS

  TYPE, extends(PARPASS) :: PARPASS1    !! Parameter passing structure 1
     INTEGER       :: a
     REAL          :: X(2,2)
   contains
     procedure :: OBJ => OBJ1
  END TYPE PARPASS1

  TYPE, extends(PARPASS) :: PARPASS2    !! Parameter passing structure 2
     REAL          :: b
     REAL          :: Y(3)
   contains
     procedure :: OBJ => OBJ2
  END TYPE PARPASS2

  abstract interface
     subroutine func(this, val) !Interface for the subroutine you want to implement
       import
       class(PARPASS), intent(in) :: this
       real, intent(out) :: val
     end subroutine func
  end interface

contains

   subroutine OBJ1(this, val)
    class(PARPASS1),INTENT(IN) :: this
    real, intent(out)          :: val
    INTEGER                    :: a
    REAL,ALLOCATABLE           :: X(:,:)
    a = this%a
    X = this%X
    val = a+X(1,1)+X(2,2)
  END subroutine OBJ1

  subroutine OBJ2(this, val)
    class(PARPASS2),INTENT(IN) :: this
    real, intent(out)          :: val
    REAL                       :: b
    REAL,ALLOCATABLE           :: Y(:)
    b = this%b
    Y = this%Y
    val = b / (Y(1)+Y(2))

  END subroutine OBJ2

END MODULE MYPAR


MODULE MYLIB

  USE MYPAR
  IMPLICIT NONE

CONTAINS

  SUBROUTINE MYSUB(sol, param)
    REAL,INTENT(OUT)           :: sol
    class(PARPASS), INTENT(IN) :: PARAM
    call param%obj(sol)
    sol = 2*sol
  END SUBROUTINE MYSUB

END MODULE MYLIB

PROGRAM MAIN

  USE MYPAR
  USE MYLIB
  IMPLICIT NONE

  INTEGER        :: am
  REAL           :: bm,Xm(2,2),Ym(3),sol1,sol2
  TYPE(PARPASS1) :: PARAM1
  TYPE(PARPASS2) :: PARAM2

  am = 1
  bm = 3.5
  Xm(1,:) = [1.0, 2.0]
  Xm(2,:) = [0.5, 2.5]
  Ym(1:3) = [0.25,0.50,0.75]

  PARAM1%a = am
  PARAM1%X = Xm
  PARAM2%b = bm
  PARAM2%Y = Ym

  CALL MYSUB(sol1, PARAM1)
  CALL MYSUB(sol2, PARAM2)
  PRINT *,sol1
  PRINT *,sol2

END PROGRAM MAIN

它使用包含过程OBJ的抽象类型,然后您的派生类型可以扩展它并实现实际的过程。然后,您可以将扩展 PARPASS 并实现类型绑定(bind)过程 OBJ 的任何类型传递给“MYSUB”,并从内部调用它,而无需为所有不同的可能性提供单独的接口(interface).

关于Fortran:将任意 "structures"传递给模块子例程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27083060/

相关文章:

function - 初学者阅读 Fortran 代码但无法理解这一点

Fortran:初始化期间自动确定字符长度

arrays - Fortran 90 声明可分配数组的差异

fortran - gfortran 中的清零

fortran - Fortran 中的二维数组串联

fortran - 使用 c_f_pointer 是 fortran 数组是否就地 reshape

调用 Fortran 例程的 C MPI 程序崩溃

fortran - 在 PURE 过程 Fortran 中调用类型绑定(bind)过程

gcc - 使用 iso_c_binding 时重命名 Fortran 模块中的子例程

fortran - 派生类型访问器函数的跨模块内联