aboutsummaryrefslogtreecommitdiff
! Copyright 2019 Wojciech Kosior
  
! This is free and unencumbered software released into the public domain.

! Anyone is free to copy, modify, publish, use, compile, sell, or
! distribute this software, either in source code form or as a compiled
! binary, for any purpose, commercial or non-commercial, and by any
! means.

! In jurisdictions that recognize copyright laws, the author or authors
! of this software dedicate any and all copyright interest in the
! software to the public domain. We make this dedication for the benefit
! of the public at large and to the detriment of our heirs and
! successors. We intend this dedication to be an overt act of
! relinquishment in perpetuity of all present and future rights to this
! software under copyright law.

! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
! OTHER DEALINGS IN THE SOFTWARE.

! For more information, please refer to <http://unlicense.org/>

MODULE quadratures

  IMPLICIT none
  
  integer(kind=8) :: subintervals = 100
  
  INTERFACE
     FUNCTION integrate(ibeg, iend, myfun, p) result(val)
       IMPLICIT none
       ! beginning of integration interval
       real(kind=8), intent(in) :: ibeg
       ! ending of integration interval
       real(kind=8), intent(in) :: iend
       ! function to be integrated
       procedure(funint) :: myfun
       ! polynomial order
       integer(kind=4), intent(in) :: p
       ! result of integration
       real(kind=8) :: val
     END FUNCTION integrate
  END INTERFACE
  
  INTERFACE
     FUNCTION quadrature(qbeg, qend, fun) result(val)
       IMPLICIT none
       real(kind=8), intent(in) :: qbeg, qend
       procedure(funint) :: fun
       real(kind=8) :: val
     END FUNCTION quadrature
  END INTERFACE
  
  INTERFACE
     FUNCTION funint(x) result(y)
       IMPLICIT none
       real(kind=8), intent(in) :: x
       real(kind=8) :: y
     END FUNCTION funint
  END INTERFACE

CONTAINS
  
  FUNCTION integrate_generic(ibeg, iend, fun, quad) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: ibeg
    real(kind=8), intent(in) :: iend
    procedure(funint) :: fun
    procedure(quadrature) :: quad
    
    real(kind=8) :: val, subinterval_width, qbeg, qend, partsums(48)
    logical :: partsums_mask(48) = .true.
    real(kind=8), allocatable :: partval[:]
    
    integer(kind=8) :: min_i, max_i, i, j, subintervals_per_thread
    integer(kind=4) :: im

    allocate(partval[*])

    subintervals_per_thread =                               &
         (subintervals + num_images() - 1) / num_images()
    
    min_i = subintervals_per_thread * (this_image() - 1) + 1
    max_i = min(subintervals, subintervals_per_thread * this_image())

    subinterval_width = (iend - ibeg) / subintervals
    
    ! compute integral using quadrature pointed by quad
    partval = 0
    
    DO i = min_i, max_i
       qend = ibeg + i * subinterval_width
       qbeg = ibeg + (i - 1) * subinterval_width
       val = quad(qbeg, qend, fun)

       DO j = 1, 48
          IF (partsums_mask(j)) THEN
             partsums_mask(j) = .false.
             partsums(j) = val
             EXIT
          END IF

          val = val + partsums(j)
          partsums_mask(j) = .true.
       END DO
    END DO

    partval = 0
    DO j = 1, 48
       IF (.not. partsums_mask(j)) partval = partval + partsums(j)
    END DO
    
    IF (this_image() == 1 .and. num_images() > 1) THEN
       sync images([(im, im = 2, num_images())])
       partval = partval + sum([(partval[im], im = 2, num_images())])
       sync images([(im, im = 2, num_images())])
    END IF

    IF (this_image() /= 1) THEN
       sync images(1)
       sync images(1)
    END IF

    val = partval[1]
    
  END FUNCTION integrate_generic

  FUNCTION newton_cotes(ibeg, iend, fun, p) result(val)
    USE, intrinsic :: ieee_arithmetic

    IMPLICIT none
    real(kind=8), intent(in) :: ibeg
    real(kind=8), intent(in) :: iend
    procedure(funint) :: fun
    integer(kind=4), intent(in) :: p
    real(kind=8) :: val
    
    procedure(quadrature), pointer :: quad
    
    SELECT CASE (p)
    CASE (0)
       quad => rectangle
    CASE (1)
       quad => trapeze
    CASE (2)
       quad => simpson_1_third
    CASE default
       val = ieee_value(val, ieee_quiet_nan)
       RETURN
    END SELECT

    val = integrate_generic(ibeg, iend, fun, quad)
  END FUNCTION newton_cotes
  
  FUNCTION rectangle(qbeg, qend, fun) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: qbeg, qend
    procedure(funint) :: fun
    real(kind=8) :: val

    val = (qend - qbeg) * fun((qend + qbeg) * 0.5)
  END FUNCTION rectangle
  
  FUNCTION trapeze(qbeg, qend, fun) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: qbeg, qend
    procedure(funint) :: fun
    real(kind=8) :: val

    val = (qend - qbeg) * 0.5 * (fun(qbeg) + fun(qend))
  END FUNCTION trapeze
  
  FUNCTION simpson_1_third(qbeg, qend, fun) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: qbeg, qend
    procedure(funint) :: fun
    real(kind=8) :: val

    val = (qend - qbeg) * (1/6.0) *                               &
         (fun(qbeg) + 4 * fun ((qbeg + qend) * 0.5) + fun(qend))
  END FUNCTION simpson_1_third

  FUNCTION gauss(ibeg, iend, fun, p) result(val)
    USE, intrinsic :: ieee_arithmetic

    IMPLICIT none
    real(kind=8), intent(in) :: ibeg
    real(kind=8), intent(in) :: iend
    procedure(funint) :: fun
    integer(kind=4), intent(in) :: p
    real(kind=8) :: val
    
    procedure(quadrature), pointer :: quad
    
    SELECT CASE (p)
    CASE (0)
       quad => gauss_n1
    CASE (1)
       quad => gauss_n2
    CASE (2)
       quad => gauss_n3
    CASE default
       val = ieee_value(val, ieee_quiet_nan)
       RETURN
    END SELECT

    val = integrate_generic(ibeg, iend, fun, quad)
  END FUNCTION gauss
  
  FUNCTION gauss_generic(mid, halfwidth, fun, points, weights)    &
       result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: mid, halfwidth, points(:), weights(:)
    procedure(funint) :: fun
    real(kind=8) :: val
    
    integer(kind=4) :: i

    val = halfwidth * sum(weights * &
         [(fun(points(i) * halfwidth + mid), i = 1, size(points))])
  END FUNCTION gauss_generic
  
  FUNCTION gauss_n1(qbeg, qend, fun) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: qbeg, qend
    procedure(funint) :: fun
    real(kind=8) :: val, weights(1) = [2], points(1) = [0]

    val = gauss_generic((qbeg + qend) * 0.5, (qend - qbeg) * 0.5, &
         fun, points, weights)
  END FUNCTION gauss_n1
  
  FUNCTION gauss_n2(qbeg, qend, fun) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: qbeg, qend
    procedure(funint) :: fun
    real(kind=8) :: val, weights(2) = [1, 1],                     &
         points(2) = [1 / sqrt(3.0), -1 / sqrt(3.0)]
         
    val = gauss_generic((qbeg + qend) * 0.5, (qend - qbeg) * 0.5, &
         fun, points, weights)
  END FUNCTION gauss_n2
  
  FUNCTION gauss_n3(qbeg, qend, fun) result(val)
    IMPLICIT none
    real(kind=8), intent(in) :: qbeg, qend
    procedure(funint) :: fun
    real(kind=8) :: val, weights(3) = [8 / 9.0, 5 / 9.0, 5 / 9.0],&
         points(3) = [0.0, sqrt(3 / 5.0), -sqrt(3 / 5.0)]

    val = gauss_generic((qbeg + qend) * 0.5, (qend - qbeg) * 0.5, &
         fun, points, weights)
  END FUNCTION gauss_n3

END MODULE quadratures