mod_integration.f90 Source File


Files dependent on this one

sourcefile~~mod_integration.f90~~AfferentGraph sourcefile~mod_integration.f90 mod_integration.f90 sourcefile~smod_integration.f90 smod_integration.f90 sourcefile~smod_integration.f90->sourcefile~mod_integration.f90 sourcefile~smod_assemble_1d.f90 smod_assemble_1D.f90 sourcefile~smod_assemble_1d.f90->sourcefile~mod_integration.f90 sourcefile~smod_assemble_2d.f90 smod_assemble_2D.f90 sourcefile~smod_assemble_2d.f90->sourcefile~mod_integration.f90

Contents

Source Code


Source Code

! Learn_dg - A quick and dirty project to deploy a working DG solver
! Copyright (c) 2017, Chris Coutinho
! All rights reserved.
!
! Licensed under the BSD-2 clause license. See LICENSE for details.

module mod_integration
    use, intrinsic  :: iso_fortran_env, only: wp=>real64
    use             :: lib_array,       only: linspace
    implicit none

    private
    public :: integrate
    interface integrate
        module procedure integrate1D
        module procedure integrate2D
    end interface integrate

    interface
        module function fun2d_interf(x, y) result(z)
            real(wp), intent(in)  :: x(:,:), y(:,:)
            real(wp), allocatable :: z(:,:)
        end function

        module function fun1d_interf(xx) result(yy)
            real(wp), intent(in)  :: xx(:)
            real(wp), allocatable :: yy(:)
        end function fun1d_interf

        module subroutine gaussquad(N, x, w)
            integer,  intent(in)  :: N
            real(wp), intent(out) :: x(N), w(N)
        end subroutine gaussquad
    end interface

contains

    module function integrate1D(fun, xbnds) result(result)
        ! This routine uses gauss-legendre quadrature to integrate a 1D
        ! line function

        ! Input/Output dummy variables
        real(wp), intent(in)    :: xbnds(2)
        real(wp)                :: result
        procedure(fun1d_interf) :: fun

        ! Local variables
        integer, parameter    :: N_start = 2
        integer               :: N
        real(wp)              :: result_old, error
        real(wp), parameter   :: eps = epsilon(0e0)
        real(wp), allocatable :: x(:), w(:), y(:)

        N = N_start
        result = 0.0_wp
        error = 1.0_wp

        do
            result_old = result

            allocate(x(N), w(N), y(N))

            call gaussquad(N, x, w)

            y = fun(x)
            result = dot_product(y, w)

            deallocate(x, w, y)

            ! error = norm2([result, result_old])
            error = sqrt((result-result_old)**2.0_wp)
            ! error = abs((result-result_old)/(result_old+eps))
            ! print*, N, result, error
            ! print*, N, result, result_old, error, eps

            ! Check if error is acceptable, as well as whether the loop
            ! was gone through at least twice (N = 3, 4, 5...)
            if ( N > N_start .and. &
                norm2( [ error ] ) <= eps ) then
                ! print'(a,i3,a,e13.5)',  'Fun integrated in ', N, &
                !                         ' iterations. Error = ', error
                exit
            else
                N = N + 1
            endif
        enddo
        return
    end function integrate1D

    module function integrate2D(fun, xbnds, ybnds) result(out)
        procedure(fun2d_interf) :: fun
        real(wp), intent(in)    :: xbnds(2), ybnds(2)
        real(wp)                :: out

        integer, parameter                    :: N_start = 4
        integer                               :: ii, N
        real(wp)                              :: out_old, error
        real(wp), parameter                   :: eps = epsilon(0e0)
        real(wp), dimension(:),   allocatable :: x, w
        real(wp), dimension(:,:), allocatable :: xx, yy
        real(wp), dimension(:,:), allocatable :: wx, wy
        real(wp), dimension(:,:), allocatable :: out_spread

        ! Adaptive integration based on 2D Gauss-Legendre Quadrature
        ii    = 1       ! Iteration number
        N     = N_start ! Initial number of points to use
        error = 1._wp   ! Initial estimate of error

        do
            ! Allocate x (positions) and w (weights) based on roots of the Legendre
            ! polynomial of order 'N'
            allocate(x(N), w(N))
            allocate(xx(N,N), yy(N,N))
            allocate(wx(N,N), wy(N,N))
            allocate(out_spread(N,N))

            call gaussquad(N, x, w)

            ! Copy the 'x' array along both the x and y axes
            xx = spread(x, dim=1, ncopies=N)
            yy = spread(x, dim=2, ncopies=N)

            ! Copy the weights along both the x and y axes as well
            wx = spread(w, dim=1, ncopies=N)
            wy = spread(w, dim=2, ncopies=N)

            ! Calculate the function at the xx and yy nodes, and
            ! multiply the result with the weights: wx and wy
            out_spread = fun(xx, yy) * wx * wy

            ! Sum up the resulting array into a single scalar output
            out = sum(reshape( out_spread, [N*N,1] ))
            ! print*, out

            ! Set error if ii > 1
            if ( ii>1 ) then
                error = out - out_old
            else
                error = 1._wp
            endif

            ! Deallocate all arrays no longer needed. They will change
            ! size in each iteration anyway

            deallocate(x, w, xx, yy, wx, wy, out_spread)

            ! If iteration counter is more than 1 then check exit
            ! criteria
            if ( N > N_start .and. &
                norm2( [ error ] ) <= eps ) then
                ! print'(a,i3,a,e13.5)', 'Fun integrated in ', N, &
                !                        ' iterations. Error = ', error
                exit
            else
                out_old = out
                ii = ii + 1
                N = N + 1
            endif

        enddo

        return
    end function

end module mod_integration