Fortran:传递任意“结构"到模块子例程 [英] Fortran: passing arbitrary "structures" to a module subroutine
问题描述
我正在尝试编写一个通用子例程以将其最小化.因为我想拥有一个通用子例程,所以目标函数可以具有不同的参数,不仅在名称上,而且在尺寸上.因此,我需要一种传递该参数结构的方法(我使用的是词结构,因为我的想法是在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屋!