rotex__utils.f Source File


Source Code

! ================================================================================================================================ !
module rotex__utils
  !! Some small utilities
  use rotex__kinds, only: dp, qp

  implicit none

  private

  public :: read_blank
  public :: assert
  public :: isint
  public :: kbn_sum
  public :: upcast
  public :: downcast
  public :: printmat
  public :: isin

  interface isint
    module procedure :: isint_r
    module procedure :: isint_c
  end interface isint

  interface kbn_sum
    module procedure :: kbn_sum_rqp
    module procedure :: kbn_sum_cdp
    module procedure :: kbn_sum_cqp
  end interface kbn_sum

  interface downcast
    module procedure :: downcast_r
    module procedure :: downcast_c
  end interface downcast

  interface upcast
    module procedure :: upcast_r
    module procedure :: upcast_c
  end interface upcast

  interface printmat
    module procedure :: printmat_i
    module procedure :: printmat_r
    module procedure :: printmat_c
  end interface printmat

! ================================================================================================================================ !
contains
! ================================================================================================================================ !

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental subroutine assert(test, message)
    use rotex__system, only: die
    implicit none
    logical,      intent(in) :: test
    character(*), intent(in) :: message
    if(test .eqv. .true.) return
    call die(message)
  end subroutine assert

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  subroutine read_blank(read_unit, num_read)
    !! Reads num_read lines from unit read_unit, not storing any information. If num_read is not supplied, read one line.
    implicit none
    integer, intent(in)           :: read_unit
    integer, intent(in), optional :: num_read
    integer :: k, n
    n = 1 ; if(present(num_read)) n = num_read
    do k = 1, n ; read(read_unit,*) ; enddo
  end subroutine read_blank

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental module function isint_r(x) result(res)
    implicit none
    real(dp), intent(in) :: x
    logical :: res
    real(dp) :: tol
    tol = 8*spacing(x)
    res = .false.
    if(abs(x - anint(x)) .gt. tol) return
    res = .true.
  end function isint_r
  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental module function isint_c(z) result(res)
    implicit none
    complex(dp), intent(in) :: z
    logical :: res
    real(dp) :: tol
    real(dp) :: a
    a = z%re
    tol = 8*spacing(a)
    res = .false.
    if(abs(z%im) .gt. tol) return
    if(abs(a - anint(a)) .gt. tol) return
    res = .true.
  end function isint_c

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental subroutine kbn_sum_rqp(summation, c, input)
    !! Improved Kahan-Babuška algorithm accumulation for summations
    implicit none
    real(qp), intent(inout) :: summation, c
    real(qp), intent(in)    :: input
    real(qp) :: t
    t = summation + input
    if(abs(summation) .ge. abs(input)) then
      c = c + (summation-t) + input
    else
      c = c + (input - t) + summation
    endif
    summation = t
  end subroutine kbn_sum_rqp
  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental subroutine kbn_sum_cdp(summation, c, input)
    !! Improved Kahan-Babuška algorithm accumulation for summations
    implicit none
    complex(dp), intent(inout) :: summation, c
    complex(dp), intent(in)    :: input
    complex(dp) :: t
    t = summation + input
    if(abs(summation) .ge. abs(input)) then
      c = c + (summation-t) + input
    else
      c = c + (input - t) + summation
    endif
    summation = t
  end subroutine kbn_sum_cdp
  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental subroutine kbn_sum_cqp(summation, c, input)
    !! Improved Kahan-Babuška algorithm accumulation for summations
    implicit none
    complex(qp), intent(inout) :: summation, c
    complex(qp), intent(in)    :: input
    complex(qp) :: t
    t = summation + input
    if(abs(summation) .ge. abs(input)) then
      c = c + (summation-t) + input
    else
      c = c + (input - t) + summation
    endif
    summation = t
  end subroutine kbn_sum_cqp

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental module subroutine downcast_r(hi, lo)
    !! Send the value of hi to lo, respecting the kind of the types
    use rotex__types, only: dp, qp
    implicit none
    real(qp), intent(in)  :: hi
    real(dp), intent(out) :: lo
    lo = real(hi, kind = dp)
  end subroutine downcast_r
  pure elemental module subroutine downcast_c(hi, lo)
    !! Send the value of hi to lo, respecting the kind of the types
    use rotex__types, only: dp, qp
    implicit none
    complex(qp), intent(in)  :: hi
    complex(dp), intent(out) :: lo
    lo = cmplx(hi%re, hi%im, kind = dp)
  end subroutine downcast_c

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental module subroutine upcast_r(lo, hi)
    !! Send the value of lo to hi, respecting the kind of the types
    use rotex__types, only: dp, qp
    implicit none
    real(dp), intent(in)  :: lo
    real(qp), intent(out) :: hi
    hi = real(lo, kind = qp)
  end subroutine upcast_r
  pure elemental module subroutine upcast_c(lo, hi)
    !! Send the value of lo to hi, respecting the kind of the types
    use rotex__types, only: dp, qp
    implicit none
    complex(dp), intent(in)  :: lo
    complex(qp), intent(out) :: hi
    hi = cmplx(lo%re, lo%im, kind = qp)
  end subroutine upcast_c

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  module subroutine printmat_i(M, funit, header)
    !! Prints a matrix to the supplied funit, otherwise print to stdout
    use rotex__kinds,  only: dp
    use rotex__system, only: stdout
    integer,      intent(in)           :: M(:,:)
    integer,      intent(in), optional :: funit
    character(*), intent(in), optional :: header
    character(9), parameter :: fmt = '(X,I7)'
    integer :: nr,nc, i, j, funit_local
    funit_local = stdout ; if(present(funit)) funit_local = funit
    nr = size(M, 1)
    nc = size(M, 2)
    write(funit_local, *)
    if(present(header)) write(funit_local, '(A)') header
    do i=1, nr
      do j=1, nc
        write(funit_local, fmt, advance = "no") M(i,j)
      enddo
      write(funit_local, *)
    enddo
  end subroutine printmat_i
  ! ------------------------------------------------------------------------------------------------------------------------------ !
  module subroutine printmat_r(M, funit, header)
    !! Prints a matrix to the supplied funit, otherwise print to stdout
    use rotex__kinds,  only: dp
    use rotex__system, only: stdout
    real(dp),     intent(in)           :: M(:,:)
    integer,      intent(in), optional :: funit
    character(*), intent(in), optional :: header
    real(dp), parameter :: absmin = 1e-5_dp
    real(dp), parameter :: absmax = 1e2_dp
    character(9) :: exp_fmt = '(X,E11.4)'
    character(9) :: flt_fmt = '(X,F11.8)'
    integer :: nr,nc, i, j, funit_local
    real(dp) :: absm
    character(:), allocatable :: fmt
    funit_local = stdout ; if(present(funit)) funit_local = funit
    nr = size(M, 1)
    nc = size(M, 2)
    write(funit_local, *)
    if(present(header)) write(funit_local, '(A)') header
    do i=1, nr
      do j=1, nc
        absm = M(i,j)
        if( (absm .le. absmin .AND. absm .ne. 0.0_dp) .OR. absm .ge. absmax ) then
          fmt = exp_fmt
        else
          fmt = flt_fmt
        endif
        write(funit_local, fmt, advance = "no") M(i,j)
      enddo
      write(funit_local, *)
    enddo
  end subroutine printmat_r
  ! ------------------------------------------------------------------------------------------------------------------------------ !
  module subroutine printmat_c(M, funit, header)
    !! Prints a matrix to the supplied funit, otherwise print to stdout
    use rotex__kinds,    only: dp
    use rotex__system,   only: stdout
    use ieee_arithmetic, only: copysign => ieee_copy_sign
    complex(dp),     intent(in)           :: M(:,:)
    integer,      intent(in), optional :: funit
    character(*), intent(in), optional :: header
    real(dp), parameter :: absmin = 1e-5_dp
    real(dp), parameter :: absmax = 1e2_dp
    character(9) :: exp_fmt = '(X,E11.4)'
    character(9) :: flt_fmt = '(X,F11.8)'
    integer :: nr,nc, i, j, funit_local
    integer :: signc
    real(dp) :: absmr, absmc
    character(:), allocatable :: fmtr, fmtc
    funit_local = stdout ; if(present(funit)) funit_local = funit
    nr = size(M, 1)
    nc = size(M, 2)
    write(funit_local, *)
    if(present(header)) write(funit_local, '(A)') header
    do i=1, nr
      do j=1, nc
        absmr = abs(M(i,j)%re)
        absmc = abs(M(i,j)%im)
        if( (absmr .le. absmin .AND. absmr .ne. 0._dp) .OR. absmr .ge. absmax ) then
          fmtr = exp_fmt
        else
          fmtr = flt_fmt
        endif
        if( (absmc .le. absmin .AND. absmc .ne. 0._dp) .OR. absmc .ge. absmax ) then
          fmtc = exp_fmt
        else
          fmtc = flt_fmt
        endif
        signc = nint(copysign(1._dp, M(i,j)%re))
        write(funit_local, fmtr,  advance = "no") M(i,j)%re
        write(funit_local, '(A)', advance = "no") " +"
        write(funit_local, fmtc,  advance = "no") absmc
        write(funit_local, '(A)', advance = "no") " im,"
      enddo
      write(funit_local, *)
    enddo
  end subroutine printmat_c

  ! ------------------------------------------------------------------------------------------------------------------------------ !
  pure elemental module function isin(x, xl, xr, lclosed, rclosed) result(res)
    !! Test whether x is in the interval spanned by x1,x2
    !! l/rclosed if true include xl and xr, respectively. They are true by default
    implicit none
    real(dp), intent(in) :: x, xl, xr
    logical, intent(in), optional :: lclosed, rclosed
    logical :: res
    logical :: lclosed_, rclosed_
    lclosed_ = .true. ; if(present(lclosed)) lclosed_ = lclosed
    rclosed_ = .true. ; if(present(rclosed)) rclosed_ = rclosed
    res = .false.
    if(x .lt. xl) return
    if(x .gt. xr) return
    if(lclosed_ .eqv. .false.) then
      if(x .eq. xl) return
    endif
    if(rclosed_ .eqv. .false.) then
      if(x .eq. xr) return
    endif
    res = .true.
  end function isin

! ================================================================================================================================ !
end module rotex__utils
! ================================================================================================================================ !