Passing additional arguments in Newton’s method in Fortran

安稳与你 提交于 2019-12-24 10:59:50

问题


I am having trouble in implementing an approach to call Newton's method in a Fortran program. So I want to use Newton's method to solve an equation following the link

However, my program is slightly different with the example above. In my case, the equation requires some additional information which are produced during runtime.

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

which means the f is calculated not only based on x, but also a few other variables (but x is the unknown).

I have a solution, which only works for a simple case: I used a module to include Newton's solver. I also defined a derived data type to hold all the arguments inside the module. It works good now.

My question is: I need to call the Newton's method many times, and each time the arguments are different. How should I design the structure of the modules? Or should I use a different solution?

I provided a simple example below:

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

And the calling module is:

!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

My understanding is that if I put the argu object inside the solver module, then all solver calling will use the same arguments (I can still save all of them inside module A using the above method). In that case, I have to update the arguments during each for loop?

Because the program runs using MPI/OpenMP, I want to make sure there is no overwritten among threads. Thank you.


回答1:


There is a common pattern in modern Fortran for the problem you are facing (partial function application). Unlike other languages, Fortran doesn't have function closures, so making a lexical scope for a function is a little "convoluted" and kind of limited.

You should really consider revisiting all the links @VladmirF shared on the comment, most of them apply straightforwardly to your case. I will give you an example of a solution.

This is a solution without using a wrapper type. I will use a feature included in Fortran 2008 standard: passing an internal procedure as an argument. It is compatible with the latest gfortran, Intel and many others. If you can't access a compiler with this feature or if you prefer a solution with a derived type, you can refer to this answer.

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

The rationale of this code is: as f and fp lay inside of the solve_quad procedure, they have access to the arguments a, b and c by host association, without touching those function's signatures. The resulting effect is like changing the arity of the function.

Testing it with gfortran 8.0 and the solver implementation from the link you shared, I got this:

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



回答2:


After discussing with a colleague, I have a solution to my question 2.

If we have only one argument object for the solver module, then all the calling will access the same arguments because they share the same memory space.

To avoid this, we want to pass the argument object as an argument into the solver. So instead of using the default solver subroutine, we will re-write the Newton's method so it can accept additional argument.

(I used the simplest Newton subroutine earlier because I wanted to keep it untouched.)

In this way, we will define an array of argument objects and pass them during runtime.

Thank you for the comments.



来源:https://stackoverflow.com/questions/52196267/passing-additional-arguments-in-newton-s-method-in-fortran

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!