evaluate.f90 Source File


Source Code

!> Author:   Jabir Ali Ouassou
!> Category: Foundation
!>
!> This module has functions that evaluate textual expressions as Fortran data.
!> This includes functions for converting mathematical expressions as arrays.

module evaluate_m
    use :: math_m
    use :: stdio_m
    private

    ! Declare which routines to export
    public :: evaluate

    ! Declare public interfaces
    interface evaluate
        module procedure &
            evaluate_scalar_value,  evaluate_scalar_field, &
            evaluate_vector_value,  evaluate_vector_field, &
            evaluate_logical_value, evaluate_integer_value
    end interface
contains
    subroutine evaluate_logical_value(expression, value)
    !!  Takes a scalar logical expression as input and returns the value.
    !!
    !!  Usage:
    !!
    !!      call evaluate_logical_value('F', output)
    !!      call evaluate_logical_value('T', output)
    !!
        character(*), intent(in)  :: expression !! Character 'T' or 'F'
        logical,      intent(out) :: value      !! Parsed expression

        select case (expression)
            case ('T', 't')
                value = .true.
            case ('F', 'f')
                value = .false.
            case default
                call error('Invalid logical expression: "'//trim(expression)//'"')
        end select
    end subroutine

    subroutine evaluate_integer_value(expression, value)
    !!  Takes a scalar integer expression as input and returns the value.
    !!
    !!  Usage:
    !!
    !!      call evaluate_integer_value('10', output)
    !!

        character(*), intent(in)  :: expression !! Numerical expression
        integer,      intent(out) :: value      !! Parsed result

        integer :: iostat

        read (expression, *, iostat=iostat) value

        if (iostat /= 0) then
            call error('Invalid integer expression: "'//trim(expression)//'"')
        end if
    end subroutine

    subroutine evaluate_scalar_value(expression, value)
    !!  Takes a scalar mathematical expression as input and returns the value.
    !!
    !!  Usage:
    !!
    !!      call evaluate_scalar_value('0',                    output)
    !!      call evaluate_scalar_value('sin(0.3*pi)*exp(-pi)', output)
    !!
        use :: fparser

        character(*), intent(in)  :: expression !! Mathematical expression
        real(wp),     intent(out) :: value      !! Parsed result

        ! Make sure the expression is non-empty
        if (scan(expression, '0123456789pi') <= 0) then
            call error('Invalid scalar expression: "'//trim(expression)//'"')
        end if

        ! Initialize the function parser
        call initf(1)
        call parsef(1, expression, ['pi'])

        ! Evaluate the parsed function
        value = evalf(1, [pi])
    end subroutine

    subroutine evaluate_scalar_field(expression, domain, value)
    !!  Evaluates a scalar-valued function of 'z' at the provided values for 'z'.
    !!
    !!  Usage:
    !!
    !!      call evaluate_scalar_value('0',                    input(1:n), output(1:n))
    !!      call evaluate_scalar_value('sin(pi*z)*exp(-pi*z)', input(1:n), output(1:n))
    !!
        use :: fparser

        character(*),           intent(in)  :: expression !! Function of 'z'
        real(wp), dimension(:), intent(in)  :: domain     !! Values of 'z'
        real(wp), dimension(:), allocatable :: value      !! Parsed result

        integer :: n

        ! Make sure the expression is non-empty
        if (scan(expression, '0123456789piz') <= 0) then
            call error('Invalid scalar expression: "'//trim(expression)//'"')
        end if

        ! Initialize the function parser
        call initf(1)
        call parsef(1, expression, ['pi', 'z '])

        ! Allocate memory for the output
        allocate (value(size(domain)))

        ! Evaluate the parsed function
        do n = 1, size(domain)
            value(n) = evalf(1, [pi, domain(n)])
        end do
    end subroutine

    subroutine evaluate_vector_value(expression, value)
    !!  Takes a vector mathematical expression as input and returns the value.
    !!
    !!  Usage:
    !!
    !!      call evaluate_scalar_value('[0,0,0]',                     output(1:3))
    !!      call evaluate_scalar_value('[sin(0.3*pi),0,cos(0,3*pi)]', output(1:3))
    !!
        use :: fparser

        character(*),           intent(in)  :: expression !! Mathematical expression
        real(wp), dimension(3), intent(out) :: value      !! Parsed result

        integer, dimension(4) :: sep

        ! Find the vector delimiters
        sep(1) = scan(expression, '[', back=.false.)
        sep(2) = scan(expression, ',', back=.false.)
        sep(3) = scan(expression, ',', back=.true.)
        sep(4) = scan(expression, ']', back=.true.)

        ! Make sure the expressions are non-empty
        if (sep(1) <= 0 .or. any(sep(2:4) - sep(1:3) <= 1)) then
            call error('Invalid vector expression: "'//trim(expression)//'"')
        end if
        if (scan(expression(sep(1) + 1:sep(2) - 1), '0123456789pi') <= 0 .or. &
            scan(expression(sep(2) + 1:sep(3) - 1), '0123456789pi') <= 0 .or. &
            scan(expression(sep(3) + 1:sep(4) - 1), '0123456789pi') <= 0) &
        then
            call error('Invalid vector expression: "'//trim(expression)//'"')
        end if

        ! Initialize the function parser
        call initf(3)
        call parsef(1, expression(sep(1) + 1:sep(2) - 1), ['pi'])
        call parsef(2, expression(sep(2) + 1:sep(3) - 1), ['pi'])
        call parsef(3, expression(sep(3) + 1:sep(4) - 1), ['pi'])

        ! Evaluate the parsed function
        value(1) = evalf(1, [pi])
        value(2) = evalf(2, [pi])
        value(3) = evalf(3, [pi])
    end subroutine

    subroutine evaluate_vector_field(expression, domain, value)
    !!  Evaluates a vector-valued function of 'z' at the provided values for 'z'.
    !!
    !!  Usage:
    !!
    !!      call evaluate_scalar_value('[0,0,0]',                     input(1:n), output(1:3,1:n))
    !!      call evaluate_scalar_value('[sin(pi*z/2),0,cos(pi*z/2)]', input(1:n), output(1:3,1:n))
    !!
        use :: fparser

        character(*),              intent(in)  :: expression !! Function of 'z'
        real(wp), dimension(:),    intent(in)  :: domain     !! Values of 'z'
        real(wp), dimension(:, :), allocatable :: value      !! Parsed result

        integer, dimension(4) :: sep
        integer               :: n

        ! Allocate memory for the output
        allocate (value(3, size(domain)))

        ! Find the vector delimiters
        sep(1) = scan(expression, '[', back=.false.)
        sep(2) = scan(expression, ',', back=.false.)
        sep(3) = scan(expression, ',', back=.true.)
        sep(4) = scan(expression, ']', back=.true.)

        ! Make sure the expressions are non-empty
        if (sep(1) <= 0 .or. any(sep(2:4) - sep(1:3) <= 1)) then
            call error('Invalid vector expression: "'//trim(expression)//'"')
        end if
        if (scan(expression(sep(1) + 1:sep(2) - 1), '0123456789piz') <= 0 .or. &
            scan(expression(sep(2) + 1:sep(3) - 1), '0123456789piz') <= 0 .or. &
            scan(expression(sep(3) + 1:sep(4) - 1), '0123456789piz') <= 0) &
        then
            call error('Invalid vector expression: "'//trim(expression)//'"')
        end if

        ! Initialize the function parser
        call initf(3)
        call parsef(1, expression(sep(1) + 1:sep(2) - 1), ['pi', 'z '])
        call parsef(2, expression(sep(2) + 1:sep(3) - 1), ['pi', 'z '])
        call parsef(3, expression(sep(3) + 1:sep(4) - 1), ['pi', 'z '])

        ! Evaluate the parsed function
        do n = 1, size(domain)
            value(1, n) = evalf(1, [pi, domain(n)])
            value(2, n) = evalf(2, [pi, domain(n)])
            value(3, n) = evalf(3, [pi, domain(n)])
        end do
    end subroutine
end module