Fortran:传递任意“结构"到模块子例程 [英] Fortran: passing arbitrary "structures" to a module subroutine

查看:44
本文介绍了Fortran:传递任意“结构"到模块子例程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to write a general use subroutine for minimization. As I want to have a general purpose subroutine, the objective functions can have different parameters, not just in names, but in dimensions too. So I need a way to pass that parameter structure (I'm using the word structure because my idea is to use something like a structure type variable in Matlab). I managed to use the derived data type, which worked just fine, but the problem arises when I have two different objective functions in the same program. This is a sample code:

在主程序main.f90中:

In the main program 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的模块

And a module called 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不能是任意的.

Obviously, if I comment the lines with CALL MYSUB(sol2,OBJ2,PARAM2) and PRINT *,sol2, my code runs smoothly. This was I had before having two "objective functions", but now when I have them it doesn't work because the derived type variable PARPASS1 in MYSUB cannot be arbitrary.

有什么想法吗?

推荐答案

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

You could use an interface and overload the subroutine 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的类型来区分功能

Then you can call it by using MYSUB and it will differentiate the functions based on the TYPE of PARAM

好的,这怎么办?

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 的任何类型,并将类型绑定过程 OBJ 实施为'MYSUB'并从内部调用它,而无需针对所有不同可能性的单独接口

It uses an abstract type that contains the procedure OBJ and then your derived types can extend that and implement the actual procedure. You can then pass any type that extends PARPASS and implements the type-bound procedure OBJ to 'MYSUB' and call it from within without having a separate interface for all the different possibilities.

这篇关于Fortran:传递任意“结构"到模块子例程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆