complex_step.f90 Source File


This file depends on

sourcefile~~complex_step.f90~~EfferentGraph sourcefile~complex_step.f90 complex_step.f90 sourcefile~kind_module.f90 kind_module.F90 sourcefile~complex_step.f90->sourcefile~kind_module.f90

Files dependent on this one

sourcefile~~complex_step.f90~~AfferentGraph sourcefile~complex_step.f90 complex_step.f90 sourcefile~fortran_astrodynamics_toolkit.f90 fortran_astrodynamics_toolkit.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~complex_step.f90

Source Code

!*****************************************************************************************
!> author: Jacob Williams
!
!  Complex-step differentiation routines.
!
!# See also
!  1. J.R.R.A. Martins, P. Sturdza, J.J. Alonso,
!     "The Complex-Step Derivative Approximation",
!     ACM Transactions on Mathematical Software,
!     Vol. 29, No. 3, September 2003, Pages 245262.

    module complex_step_module

    use kind_module,    only: wp

    implicit none

    private

    interface
        function func(x) result(f)
        import :: wp
        implicit none
        complex(wp),intent(in) :: x
        complex(wp) :: f
        end function func
    end interface

    public :: complex_step_derivative
    public :: complex_step_test  !for testing

    contains
!*****************************************************************************************

!*****************************************************************************************
!>
!  Compute the first derivative using the complex-step method.
!  This is Equation 6 from Reference [1].

    subroutine complex_step_derivative(f,x,h,dfdx)

    implicit none

    procedure(func)        :: f
    complex(wp),intent(in) :: x
    real(wp),intent(in)    :: h
    real(wp),intent(out)   :: dfdx

    dfdx = AIMAG(f(cmplx(real(x,wp),AIMAG(x)+h,wp)))/h

    end subroutine complex_step_derivative
!*****************************************************************************************

!*****************************************************************************************
!>
!  Compute the first derivative using a forward difference.
!  This is Equation 1 from Reference [1].

    subroutine forward_diff(f,x,h,dfdx)

    implicit none

    procedure(func)        :: f
    complex(wp),intent(in) :: x
    real(wp),intent(in)    :: h
    real(wp),intent(out)   :: dfdx

    dfdx = (f(x+h) - f(x)) / h

    end subroutine forward_diff
!*****************************************************************************************

!*****************************************************************************************
!>
!  Compute the first derivative using a 2-point central difference [-h,h].

    subroutine central_diff(f,x,h,dfdx)

    implicit none

    procedure(func)        :: f
    complex(wp),intent(in) :: x
    real(wp),intent(in)    :: h
    real(wp),intent(out)   :: dfdx

    dfdx = (f(x+h) - f(x-h)) / (2.0_wp*h)

    end subroutine central_diff
!*****************************************************************************************

!*****************************************************************************************
!>
!  Compute the first derivative using a 4-point central difference [-2h,-h,h,2h].

    subroutine central_diff_4(f,x,h,dfdx)

    implicit none

    procedure(func)        :: f
    complex(wp),intent(in) :: x
    real(wp),intent(in)    :: h
    real(wp),intent(out)   :: dfdx

    real(wp) :: h2

    h2 = 2.0_wp * h

    dfdx = (f(x-h2) - 8.0_wp*f(x-h) + 8.0_wp*f(x+h) - f(x+h2)) / (12.0_wp*h)

    end subroutine central_diff_4
!*****************************************************************************************

!*****************************************************************************************
!>
!  Unit test for the complex_step module.

    subroutine complex_step_test()

    implicit none

    integer     :: i
    complex(wp) :: x
    real(wp)    :: h,dfdx,dfdx2,dfdx3,dfdx4,err,err2,err3,err4

    write(*,*) ''
    write(*,*) '---------------'
    write(*,*) ' complex_step_test'
    write(*,*) '---------------'
    write(*,*) ''

    x = cmplx(2.0_wp,0.0_wp,wp)
    h = 1.0e-10_wp

    call complex_step_derivative(test_func,x,h,dfdx)

    !write(*,*) ''
    !write(*,*) 'x     :',x
    !write(*,*) 'dfdx  :',dfdx
    !write(*,*) 'error :',real(test_deriv(x),wp) - dfdx
    !write(*,*) ''

    write(*,'(*(A30))') 'h', 'forward diff err', 'central diff err', 'central diff 4 err', 'complex step err'

    do i=1,200
        h = 10.0_wp**(-i/10.0_wp)
        call complex_step_derivative(test_func,x,h,dfdx)
        call forward_diff(test_func,x,h,dfdx2)
        call central_diff(test_func,x,h,dfdx3)
        call central_diff_4(test_func,x,h,dfdx4)
        err  = real(test_deriv(x),wp) - dfdx
        err2 = real(test_deriv(x),wp) - dfdx2
        err3 = real(test_deriv(x),wp) - dfdx3
        err4 = real(test_deriv(x),wp) - dfdx4
        write(*,'(*(E30.16,1X))') h, err2, err3, err4, err
    end do

    contains
!*****************************************************************************************

    !****************************************
        function test_func(x) result(f)

        implicit none

        complex(wp),intent(in) :: x
        complex(wp) :: f

        f = exp(x) + sin(x)

        end function test_func
    !****************************************

    !****************************************
        function test_deriv(x) result(f)

        implicit none

        complex(wp),intent(in) :: x
        complex(wp) :: f

        f = exp(x) + cos(x)

        end function test_deriv
    !****************************************

    end subroutine complex_step_test
!*****************************************************************************************

!*****************************************************************************************
    end module complex_step_module
!*****************************************************************************************