Skip to content

Instantly share code, notes, and snippets.

@DSCF-1224
Last active February 23, 2021 22:09
Show Gist options
  • Save DSCF-1224/7a489092fb6c6a163455b28d7c21edde to your computer and use it in GitHub Desktop.
Save DSCF-1224/7a489092fb6c6a163455b28d7c21edde to your computer and use it in GitHub Desktop.
Newton法による1変数方程式の数値解法の実装

Newton法による1変数方程式の数値解法の実装

module: mod_eqn_func > subroutine: eqn_f

求めたい方程式の解は eqn_f(x)=0 を満たすものとする。

module: mod_eqn_func > subroutine: eqn_df

求めたい方程式の解は eqn_f(x)=0 を満たすものとする。 eqn_df(x)eqn_f(x) の1階導関数とする。

module: mod_solver > subroutine: solver_newton

argument description
file 数値解を保存するファイルのパス
solution_init obj_func(x)=0 となるような x の初期条件
num_trial_max obj_func(x)=0 となるような x の更新回数の上限
allowable_error obj_func(x)=0 となるような x の収束判定値(前回の数値解との相対誤差)

ファイル構成

  • cd
    • Makefile
    • main.f08
    • mod_eqn_func.f08
    • mod_solver.f08
    • mod_utility.f08

参考文献

Homotopy法による非線形方程式の解法

! ==================================================================================================================================
!
! [reference]
! https://www.slideshare.net/HideoHirose/homotopy-67988638
!
! ==================================================================================================================================
program main
! <module>s to import
use, intrinsic :: iso_fortran_env
use, non_intrinsic :: mod_solver
! require all variables to be explicitly declared
implicit none
call solver_newton(file= './result.bin', solution_init= 0.0e+000_REAL64, num_trial_max= 20, allowable_error = 1.0e-015_REAL64)
end program main
! ==================================================================================================================================
! EOF
! ==================================================================================================================================
FCFLAGS = -ffree-line-length-none -O2 -pedantic -std=f2008 -Wall -Wextra # -fbacktrace -fbounds-check
OBJS = ./mod_utility.o ./mod_eqn_func.o ./mod_solver.o ./main.o
main.exe: $(OBJS)
gfortran $(FCFLAGS) -o ./main.exe $(OBJS)
%.o: %.f08
gfortran $(FCFLAGS) -c $<
clean:
rm ./*.mod ./*.o
! ==================================================================================================================================
!
! [reference]
! https://www.slideshare.net/HideoHirose/homotopy-67988638
!
! ==================================================================================================================================
module mod_eqn_func
! <module>s to import
use, intrinsic :: iso_fortran_env
! require all variables to be explicitly declared
implicit none
! contained <subroutine>s and <function>s are below
contains
pure function eqn_f (x)
! argument(s) for this <function>
real(REAL64), intent(in) :: x
! return value of this <function>
real(REAL64) :: eqn_f
eqn_f = x - exp( - x )
end function eqn_f
pure function eqn_df (x)
! argument(s) for this <function>
real(REAL64), intent(in) :: x
! return value of this <function>
real(REAL64) :: eqn_df
eqn_df = 1.0_REAL64 + exp( - x )
end function eqn_df
end module mod_eqn_func
! ==================================================================================================================================
! EOF
! ==================================================================================================================================
! ==================================================================================================================================
!
! [reference]
! https://www.slideshare.net/HideoHirose/homotopy-67988638
!
! ==================================================================================================================================
module mod_solver
! <module>s to import
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
use, non_intrinsic :: mod_utility
use, non_intrinsic :: mod_eqn_func
! require all variables to be explicitly declared
implicit none
! <type>s for this <module>
type typ_num_trial
integer(INT32) :: max
integer(INT32) :: end
end type typ_num_trial
type typ_equation
real(REAL64) :: self
real(REAL64) :: drv
end type typ_equation
type typ_solution
real(REAL64) :: self
real(REAL64) :: error
end type typ_solution
type typ_trial
type(typ_equation) :: equation
type(typ_solution) :: solution
end type typ_trial
type typ_result
type(typ_num_trial) , private :: num_trial
type(typ_trial) , private , allocatable :: trial(:)
end type typ_result
! constant(s) for this <module>
integer(INT32) , parameter , private :: num_trial_begin = 0_INT32
! accessibility of the <subroutine>s and <function>s in this <module>
! kind: function
private :: eval_convergence
private :: get_num_trial_end
! kind: subroutine
private :: eval_allowable_error
private :: eval_num_tiral_max
private :: eval_solution_init
private :: initialize_instance
private :: save_result
public :: solver_newton
private :: update_solution_newton
! contained <subroutine>s and <function>s are below
contains
pure function eval_convergence (trial_next, trial_pre, allowable_error) result(stat)
! argument(s) for this <subroutine>
type(typ_trial) , intent(in) :: trial_next
type(typ_trial) , intent(in) :: trial_pre
real(REAL64) , intent(in) :: allowable_error
! return value of this <function>
logical :: stat
stat = ieee_is_nan( trial_next%solution%self )
if (stat) then
return
else
stat = ( trial_next%solution%error .le. allowable_error * abs(trial_pre%solution%self) )
end if
return
end function eval_convergence
pure function get_num_trial_end (instance) result(num_trial_end)
! argument(s) for this <function>
type(typ_result) , intent(in) :: instance
! return value of this <function>
integer(INT32) :: num_trial_end
num_trial_end = instance%num_trial%end
end function
subroutine eval_allowable_error (val)
! argument(s) for this <subroutine>
real(REAL64) , intent(in) :: val
if ( .not. ieee_is_finite(val) ) then
write(unit= ERROR_UNIT, fmt='(A)') 'The allowable error of the solution must be a finite IEEE value.'
stop
else if ( val .ge. 1.0_REAL64 ) then
write(unit= ERROR_UNIT, fmt='(A)') 'The allowable error of the solution must be less then 1.'
stop
end if
return
end subroutine eval_allowable_error
subroutine eval_num_tiral_max (val)
! argument(s) for this <subroutine>
integer(INT32) , intent(in) :: val
select case (val)
case(:0)
write(unit= ERROR_UNIT, fmt='(A)') 'The number of steps to update the solution must be greater than zero.'
stop
case default
return
end select
end subroutine eval_num_tiral_max
subroutine eval_solution_init (val)
! argument(s) for this <subroutine>
real(REAL64) , intent(in) :: val
if ( .not. ieee_is_finite(val) ) then
write(unit= ERROR_UNIT, fmt='(A)') 'The initial condition of the solution must be a finite IEEE value.'
stop
end if
return
end subroutine eval_solution_init
subroutine initialize_instance (instance, num_trial_max, solution_init)
! argument(s) for this <subroutine>
type(typ_result) , intent(inout) :: instance
integer(INT32) , intent(in) :: num_trial_max
real(REAL64) , intent(in) :: solution_init
! variable(s) for this <subroutine>
integer :: buffer_stat
type(typ_equation) :: init_equation
type(typ_solution) :: init_solution
type(typ_trial) :: init_trial
! STEP.01
! prepare support instances to initialize the target instance
init_equation % self = ieee_value(x= solution_init, class= ieee_quiet_nan)
init_equation % drv = ieee_value(x= solution_init, class= ieee_quiet_nan)
init_solution % self = ieee_value(x= solution_init, class= ieee_quiet_nan)
init_solution % error = ieee_value(x= solution_init, class= ieee_quiet_nan)
init_trial % equation = init_equation
init_trial % solution = init_solution
! STEP.02
! try to allocate the member arrays
allocate( instance%trial(num_trial_begin:num_trial_max), stat= buffer_stat, source= init_trial )
call eval_stat_allocate(stat= buffer_stat, name= 'typ_result%trial(:)' )
! STEP.03
! initialize the member variables
instance % num_trial % max = num_trial_max
instance % num_trial % end = num_trial_max
instance % trial(num_trial_begin) % solution % self = solution_init
instance % trial(num_trial_begin) % equation % self = eqn_f ( solution_init )
instance % trial(num_trial_begin) % equation % drv = eqn_df ( solution_init )
! STEP.END
return
end subroutine initialize_instance
subroutine save_result (unit, instance)
! argument(s) for this <subroutine>
integer , intent(in) :: unit
type(typ_result) , intent(in) :: instance
! variable(s) for this <subroutine>
integer(INT32) :: iter_trial
do iter_trial = num_trial_begin , get_num_trial_end(instance) , 1_INT32
write(unit= unit, fmt='(I3,4(1X,ES24.16E3))') &!
iter_trial , &!
instance%trial(iter_trial)%solution%self , &!
instance%trial(iter_trial)%solution%error , &!
instance%trial(iter_trial)%equation%self , &!
instance%trial(iter_trial)%equation%drv
end do
end subroutine save_result
subroutine solver_newton (file, solution_init, num_trial_max, allowable_error)
! argument(s) for this <subroutine>
character(len=*) , intent(in) :: file ! file path to save the solution
real(REAL64) , intent(in) :: solution_init ! initial condition of the newton method
integer(INT32) , intent(in) :: num_trial_max ! number of trials to update the numerical solution
real(REAL64) , intent(in) :: allowable_error ! allowable error of the numerical solution
! variable(s) (scalar) for this <subroutine>
integer :: buffer_stat ! unit number to save the result
integer :: save_unit ! unit number to save the result
integer(INT32) :: iter_trial ! iterator
integer(INT32) :: indx_trial_pre ! index to store the pre step' one
! variable(s) (vector) for this <subroutine>
type(typ_result) :: result
! STEP.01
! check given arguments
call eval_allowable_error (val= allowable_error)
call eval_num_tiral_max (val= num_trial_max)
call eval_solution_init (val= solution_init)
! STEP.02
! initialize the instance to store the result
call initialize_instance (instance= result, num_trial_max= num_trial_max, solution_init= solution_init)
! STEP.03
! open a file to save the result
open(&!
action = 'write' , &!
file = file , &!
form = 'formatted' , &!
iostat = buffer_stat , &!
newunit = save_unit , &!
status = 'replace' &!
)
call eval_iostat_open(iostat= buffer_stat, file= file)
! STEP.04
! compute the numerical solution using Newton's method
do iter_trial = (num_trial_begin + 1_INT32) , num_trial_max , 1_INT32
indx_trial_pre = iter_trial - 1_INT32
call update_solution_newton( trial_next= result%trial(iter_trial), trial_pre= result%trial(indx_trial_pre) )
if ( eval_convergence(trial_next= result%trial(iter_trial), trial_pre= result%trial(indx_trial_pre), allowable_error= allowable_error) ) then
result%num_trial%end = iter_trial
exit
end if
end do
! STEP.05
! save the computed result
call save_result(unit= save_unit, instance= result)
! STEP.06
! close the file to save the result
close(unit= save_unit, status='keep')
! STEP.END
return
end subroutine solver_newton
subroutine update_solution_newton (trial_next, trial_pre)
! argument(s) for this <subroutine>
type(typ_trial) , intent(inout) :: trial_next
type(typ_trial) , intent(in) :: trial_pre
trial_next%solution%error = trial_pre%equation%self / trial_pre%equation%drv
trial_next%solution%self = trial_pre%solution%self - trial_next%solution%error
trial_next%solution%error = abs( trial_next%solution%error )
trial_next%equation%self = eqn_f ( trial_next%solution%self )
trial_next%equation%drv = eqn_df ( trial_next%solution%self )
end subroutine
end module mod_solver
! ==================================================================================================================================
! EOF
! ==================================================================================================================================
! ==================================================================================================================================
!
! [reference]
! https://www.slideshare.net/HideoHirose/homotopy-67988638
!
! ==================================================================================================================================
module mod_utility
! <module>s to import
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
! require all variables to be explicitly declared
implicit none
! constant(s) for this <module>
integer , parameter , private :: iostat_open_success = 0
integer , parameter , private :: stat_allocate_success = 0
! accessibility of the <subroutine>s and <function>s in this <module>
! kind: function
! kind: subroutine
public :: eval_iostat_open
public :: eval_stat_allocate
! contained <subroutine>s and <function>s are below
contains
subroutine eval_iostat_open (iostat, file)
! argument(s) for this <subroutine>
integer , intent(in) :: iostat
character(len=*) , intent(in) :: file
select case (iostat)
case(iostat_open_success)
return
case default
write(unit= ERROR_UNIT, fmt='(A)') 'An error was detected to open a file.'
write(unit= ERROR_UNIT, fmt='(A,1X,A)') 'FILE >' , file
write(unit= ERROR_UNIT, fmt='(A,1X,I0)') 'STATUS >' , iostat
stop
end select
end subroutine eval_iostat_open
subroutine eval_stat_allocate (stat, name)
! argument(s) for this <subroutine>
integer , intent(in) :: stat
character(len=*) , intent(in) :: name
select case (stat)
case(stat_allocate_success)
return
case default
write(unit= ERROR_UNIT, fmt='(A)') 'Failed to allocate the array to save the numerical solutions.'
write(unit= ERROR_UNIT, fmt='(A,A)') 'NAME > ', name
write(unit= ERROR_UNIT, fmt='(A,I0)') 'STATUS > ', stat
stop
end select
end subroutine eval_stat_allocate
end module mod_utility
! ==================================================================================================================================
! EOF
! ==================================================================================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment