在 Fortran 的牛顿方法中传递附加参数

Passing additional arguments in Newton’s method in Fortran

我在实现在 Fortran 程序中调用牛顿法的方法时遇到问题。 所以我想用牛顿法求解the link

后的方程

但是,我的程序与上面的例子略有不同。在我的例子中,方程式需要一些在运行时产生的额外信息。

subroutine solve(f, fp, x0, x, iters, debug)

这意味着f不仅是根据x计算的,还有一些其他变量(但x是未知数)。

我有一个解决方案,只适用于一个简单的案例: 我使用了一个模块来包含牛顿求解器。我还定义了一个派生数据类型来保存模块内的所有参数。现在效果很好。

我的问题是:我需要多次调用牛顿法,每次的参数都不一样。我应该如何设计模块的结构?或者我应该使用不同的解决方案?

我在下面提供了一个简单的例子:

module solver
  type argu
    integer :: m
  end type argu
  type(argu):: aArgu_test  !should I put here?
  contains
    subroutine solve(f, fp, x0, x, iters, debug)
       ...
       !m is used inside here
    end subroutine solve
    subroutine set_parameter(m_in)
       aArgu%m = m_in
    end subroutine set_parameter()

end module solver

调用模块是:

!only one set of argument, but used many times
module A
   use module solver

   do i = 1, 4, 1
     set_parameter(i) 
     !call Newtow method
     ...
   enddo
end module A

!can I use an array for argu type if possible?
module B
   use module solver
   type(argu), dimension(:), allocable :: aArgu ! or should I put here or inside solver module?

end module B

我的理解是,如果我将 argu 对象放在求解器模块中,那么所有求解器调用都将使用相同的参数(我仍然可以使用上述方法将它们全部保存在模块 A 中)。在那种情况下,我必须在每个 for 循环期间更新参数?

因为程序运行使用MPI/OpenMP,我想确保线程之间没有覆盖。 谢谢。

经过与同事的讨论,我的问题2有了解决方案

如果求解器模块只有一个参数对象,那么所有调用都将访问相同的参数,因为它们共享相同的内存space。

为避免这种情况,我们希望将参数对象作为参数传递给求解器。 因此,我们将不使用默认的求解器子程序,而是重写牛顿法,使其可以接受额外的参数。

(我之前使用了最简单的牛顿子程序,因为我想保持原状。)

这样,我们将定义一个参数对象数组,并在运行时传递它们。

感谢您的评论。

在现代 Fortran 中有一个通用模式可以解决您面临的问题 (partial function application)。与其他语言不同,Fortran 没有函数闭包,因此为函数创建词法作用域有点 "convoluted" 并且有点受限。

您真的应该考虑重新访问评论中分享的所有 link@VladmirF,其中大部分都直接适用于您的案例。我会给你一个解决方案的例子。

这是一个不使用包装类型的解决方案。我将使用 Fortran 2008 标准中包含的一项功能:将内部过程作为参数传递。它与最新的 gfortran、Intel 和许多其他公司兼容。 如果您无法访问具有此功能的编译器,或者您更喜欢具有派生类型的解决方案,您可以参考 答案。

module without_custom_type
  use, intrinsic :: iso_fortran_env, only: r8 => real64
  use :: solver

contains
  subroutine solve_quad(a, b, c, x0, x, iters, debug)
    integer, intent(in) :: a, b, c
    real(r8), intent(in) :: x0
    real(r8), intent(out) :: x
    integer, intent(out) :: iters
    logical, intent(in) :: debug

    call solve(f, fp, x0, x, iters, debug)

  contains
    real(r8) function f(x)
      real(r8),intent(in) :: x
      f = a * x * x + b * x + c
    end

    real(r8) function fp(x)
      real(r8),intent(in) :: x
      fp = 2 * a * x + b
    end
  end
end

此代码的基本原理是:由于 ffp 位于 solve_quad 过程内部,它们可以访问参数 abc 通过主机关联,而不触及这些函数的签名。产生的效果就像改变函数的元数一样。

使用 gfortran 8.0 和您分享的 link 中的 solver 实现对其进行测试,我得到了这个:

program test
  use, intrinsic :: iso_fortran_env, only: r8 => real64
  use :: without_custom_type
  implicit none

  real(r8) :: x, x0
  integer :: iters
  integer :: a = 1, b = -5, c = 4

  x0 = 0
  call solve_quad(a, b, c, x0, x, iters, .false.)
  print *, x, iters
  ! output: 1.0000000000000000, 5

  x0 = 7
  call solve_quad(a, b, c, x0, x, iters, .false.)
  print *, x, iters
  ! output: 4.0000000000000000, 6    
end