Commit 9b2b6136 authored by Graham, Aaron's avatar Graham, Aaron
Browse files

Merge branch 'function_tabulation' into 'master'

Add FunctionTableGenerator

See merge request futility/Futility!361
parents 2ec09ea4 a9709d69
Pipeline #189788 passed with stage
in 2 minutes and 10 seconds
......@@ -103,6 +103,7 @@ TRIBITS_ADD_LIBRARY(Utils
SchemaParser.f90
SpeciesElements.f90
FutilityComputingEnvironment.f90
FunctionTableGenerator.f90
NonLinearSolver.f90
SolutionAcceleration.f90
AndersonAcceleration.f90
......
......@@ -22,6 +22,7 @@ PUBLIC :: GreatestCommonDivisor
PUBLIC :: LeastCommonMultiple
PUBLIC :: ATAN2PI
PUBLIC :: RotateQtrClockwise
PUBLIC :: ThirdOrderBickley
INTERFACE LeastCommonMultiple
MODULE PROCEDURE LeastCommonMultiple_scalar
......@@ -245,7 +246,7 @@ ELEMENTAL FUNCTION ATAN2PI(x,y) RESULT(theta)
ENDFUNCTION ATAN2PI
!
!-------------------------------------------------------------------------------
!> @brief This routine will rotate the input x and y coordinates @nrot number of
!> @brief This routine will rotate the input x and y coordinates @nrot number of
!> clockwise quarter rotations.
!> @param x the x-coordinate
!> @param y the y-coordinate
......@@ -253,7 +254,7 @@ ENDFUNCTION ATAN2PI
!>
!> @note: Values from [-3,-1] will be handled as well, even though they
!> represent counter clockwise rotations. If nrot == 0, no rotations are
!> performed. The results of these three rotations were taken from the
!> performed. The results of these three rotations were taken from the
!> rotation matrix:
!> [cos(theta) -sin(theta) * [x
!> sin(theta) cos(theta)] y] where theta is degrees, CW.
......@@ -279,4 +280,83 @@ ELEMENTAL SUBROUTINE RotateQtrClockwise(x,y,nrot)
ENDSELECT
ENDSUBROUTINE RotateQtrClockwise
!
!-------------------------------------------------------------------------------
!> @brief It calculates third order Bickley function
!> @param x The argument of the third order Bickley function
!> @returns y The result of the third order Bickley function
!>
PURE FUNCTION ThirdOrderBickley(x) RESULT(y)
REAL(SRK),INTENT(IN) :: x
REAL(SRK) :: y
!local variables
REAL(SRK) :: xx
xx=ABS(x)
IF(xx < 0.05_SRK) THEN !Most common case
y=(0.7266088_SRK*xx-0.9990226_SRK)*xx+0.7853961_SRK
ELSEIF(xx > 9.0_SRK) THEN !Second most common case
y=0.0_SRK
ELSEIF(xx < 0.10_SRK) THEN
y=(0.6466375_SRK*xx-0.9912340_SRK)*xx+0.7852024_SRK
ELSEIF(xx < 0.15_SRK) THEN
y=(0.5856605_SRK*xx-0.9791293_SRK)*xx+0.7845986_SRK
ELSEIF(xx < 0.20_SRK) THEN
y=(0.5346648_SRK*xx-0.9638914_SRK)*xx+0.7834577_SRK
ELSEIF(xx < 0.25_SRK) THEN
y=(0.4907827_SRK*xx-0.9463843_SRK)*xx+0.7817094_SRK
ELSEIF(xx < 0.30_SRK) THEN
y=(0.4521752_SRK*xx-0.9271152_SRK)*xx+0.7793031_SRK
ELSEIF(xx < 0.35_SRK) THEN
y=(0.4177388_SRK*xx-0.9064822_SRK)*xx+0.7762107_SRK
ELSEIF(xx < 0.40_SRK) THEN
y=(0.3869945_SRK*xx-0.8849865_SRK)*xx+0.7724519_SRK
ELSEIF(xx < 0.45_SRK) THEN
y=(0.3590753_SRK*xx-0.8626685_SRK)*xx+0.7679903_SRK
ELSEIF(xx < 0.50_SRK) THEN
y=(0.3338676_SRK*xx-0.8400133_SRK)*xx+0.7628988_SRK
ELSEIF(xx < 0.60_SRK) THEN
y=(0.2998569_SRK*xx-0.8054172_SRK)*xx+0.7540982_SRK
ELSEIF(xx < 0.70_SRK) THEN
y=(0.2609154_SRK*xx-0.7587821_SRK)*xx+0.7401279_SRK
ELSEIF(xx < 0.80_SRK) THEN
y=(0.2278226_SRK*xx-0.7125290_SRK)*xx+0.7239594_SRK
ELSEIF(xx < 0.90_SRK) THEN
y=(0.1994999_SRK*xx-0.6672761_SRK)*xx+0.7058777_SRK
ELSEIF(xx < 1.00_SRK) THEN
y=(0.1751248_SRK*xx-0.6234536_SRK)*xx+0.6861762_SRK
ELSEIF(xx < 1.40_SRK) THEN
y=((-0.05337485_SRK*xx+0.3203223_SRK)*xx-0.7538355_SRK)*xx+0.7247294_SRK
ELSEIF(xx < 1.80_SRK) THEN
y=((-0.03146833_SRK*xx+0.2295280_SRK)*xx-0.6279752_SRK)*xx+0.6663720_SRK
ELSEIF(xx < 2.20_SRK) THEN
y=((-0.01906198_SRK*xx+0.1631667_SRK)*xx-0.5094124_SRK)*xx+0.5956163_SRK
ELSEIF(xx < 2.60_SRK) THEN
y=((-0.01174752_SRK*xx+0.1152418_SRK)*xx-0.4046007_SRK)*xx+0.5191031_SRK
ELSEIF(xx < 3.0_SRK) THEN
y=((-0.007328415_SRK*xx+0.08097913_SRK)*xx-0.3159648_SRK)*xx+0.4425954_SRK
ELSEIF(xx < 3.40_SRK) THEN
y=((-0.004617254_SRK*xx+0.05669960_SRK)*xx-0.2434341_SRK)*xx+0.3703178_SRK
ELSEIF(xx < 3.80_SRK) THEN
y=(0.007923547_SRK*xx-0.07158569_SRK)*xx+0.1684022_SRK
ELSEIF(xx < 4.20_SRK) THEN
y=(0.005095111_SRK*xx-0.05016344_SRK)*xx+0.1278307_SRK
ELSEIF(xx < 4.60_SRK) THEN
y=(0.003286040_SRK*xx-0.03501524_SRK)*xx+0.09611422_SRK
ELSEIF(xx < 5.00_SRK) THEN
y=(0.002126242_SRK*xx-0.02437465_SRK)*xx+0.07170491_SRK
ELSEIF(xx < 5.80_SRK) THEN
y=(0.001123687_SRK*xx-0.01425519_SRK)*xx+0.04616317_SRK
ELSEIF(xx < 6.60_SRK) THEN
y=(4.762937e-4_SRK*xx-6.810124e-3_SRK)*xx+0.02475115_SRK
ELSEIF(xx < 7.40_SRK) THEN
y=(2.031843e-4_SRK*xx-3.232035e-3_SRK)*xx+0.01302864_SRK
ELSEIF(xx < 8.20_SRK) THEN
y=(8.701440e-5_SRK*xx-1.524126e-3_SRK)*xx+6.749972e-3_SRK
ELSE !Range 8.2 <= xx <= 9.0, but the > 9.0 portion was already handled
y=(3.742673e-5_SRK*xx-7.157367e-4_SRK)*xx+3.454768e-3_SRK
ENDIF
ENDFUNCTION ThirdOrderBickley
!
ENDMODULE ExtendedMath
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
!   Futility Development Group    !
!              All rights reserved.           !
!                         !
! Futility is a jointly-maintained, open-source project between the University !
! of Michigan and Oak Ridge National Laboratory.  The copyright and license !
! can be found in LICENSE.txt in the head directory of this repository.   !
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
!> @brief This module defines the @c FunctionTable object and a factory subroutine
!> to generate the table
!>
!> The @c generateFunctionTable function should be called to retrieve a pointer
!> to the new @c FunctionTable. The function to be tabluated is provided as an
!> argument. A parameter list provides such parameters as the minimum and maximum
!> values for which the function should be tabulated, behavior for values outside
!> that range, and generation of the table. The table can be generated using
!> fixed spacing, or it can be dynamically refined until client-specified criteria
!> such as maximum error are achieved.
!>
!> Once the @c FunctionTable pointer is returned to the client, that client is
!> now the owner of the pointer and the memory. This module provides no
!> mechanisms for memory management of the table. The client should call
!> @c evaluate on the table to obtain the value of the function for an input.
!> Once the client is done with the table, it should call @c clear, then
!> deallocate the pointer.
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!
MODULE FunctionTableGeneratorModule
#include "Futility_DBC.h"
USE Futility_DBC
USE IntrType
USE Constants_Conversion
USE IO_Strings
USE ParameterLists
USE FutilityComputingEnvironmentModule
IMPLICIT NONE
PRIVATE
PUBLIC :: FunctionTable
PUBLIC :: generateFunctionTable
#ifdef UNIT_TEST
PUBLIC :: generateTableData
PUBLIC :: runTestFunctionTableGenerator
INTERFACE
MODULE SUBROUTINE runTestFunctionTableGenerator()
ENDSUBROUTINE runTestFunctionTableGenerator
ENDINTERFACE
#endif
INTERFACE generateFunctionTable
MODULE PROCEDURE generateFunctionTable1D
ENDINTERFACE generateFunctionTable
!> @brief abstract interface for a 1D tabulated function
!> @param x the input value
!> @returns y the output value
ABSTRACT INTERFACE
FUNCTION tabulatedFunction1D(x) RESULT(y)
IMPORT :: SRK
REAL(SRK),INTENT(IN) :: x
REAL(SRK) :: y
ENDFUNCTION tabulatedFunction1D
ENDINTERFACE
!> @brief Stores tabulated data for a function and returns the approximate function value
TYPE :: FunctionTable
PRIVATE
!> Logical indicating initialization status
LOGICAL(SBK) :: isInit=.FALSE.
!> Logical indicating if the table has been populated with data
LOGICAL(SBK) :: hasData=.FALSE.
!> Logical indicating if an error should be thrown for input values greater than @c maxRange
LOGICAL(SBK) :: boundsErrorHigh=.TRUE.
!> Logical indicating if an error should be thrown for input values less than @c minRange
LOGICAL(SBK) :: boundsErrorLow=.TRUE.
!> Number of data points contained in the table
INTEGER(SIK) :: nPoints=0_SIK
!> The minimum input value stored on the table
REAL(SRK) :: minRange=HUGE(ZERO)
!> The maximum input value stored on the table
REAL(SRK) :: maxRange=-HUGE(ZERO)
!> The return value for inputs greater than @c maxRange if @c boundsErrorHigh is false
REAL(SRK) :: highDefaultValue=ZERO
!> The return value for inputs greater than @c minRange if @c boundsErrorLow is false
REAL(SRK) :: lowDefaultValue=ZERO
!> The spacing between input values stored by the table
REAL(SRK) :: spacing=-HUGE(ZERO)
!> The inverse of @c spacing; stored for optimization purposes
REAL(SRK) :: inverseSpacing=-HUGE(ZERO)
!> The list of input values for which the function has been explicitly evaluated
REAL(SRK),ALLOCATABLE :: funcCoordinates(:)
!> The list of function values calculated from @c funcCoordinates and used for interpolation
REAL(SRK),ALLOCATABLE :: funcValues(:)
!> Pointer to the function used for tabulation
PROCEDURE(tabulatedFunction1D),NOPASS,POINTER :: func => NULL()
!> Pointer to the computing environment
CLASS(FutilityComputingEnvironment),POINTER :: ce => NULL()
!
CONTAINS
!> @copybrief FunctionTableGeneratorModule::initFunctionTable1D
!> @copydetails FunctionTableGeneratorModule::initFunctionTable1D
PROCEDURE,PASS :: init => initFunctionTable1D
!> @copybrief FunctionTableGeneratorModule::clearFunctionTable1D
!> @copydetails FunctionTableGeneratorModule::clearFunctionTable1D
PROCEDURE,PASS :: clear => clearFunctionTable1D
!> @copybrief FunctionTableGeneratorModule::evaluateFunctionTable1D
!> @copydetails FunctionTableGeneratorModule::evaluateFunctionTable1D
PROCEDURE,PASS :: evaluate => evaluateFunctionTable1D
ENDTYPE FunctionTable
CHARACTER(LEN=*),PARAMETER :: modName='FunctionTableGenerator'
!
!===============================================================================
CONTAINS
!
!-------------------------------------------------------------------------------
!> @brief generates a @c FunctionTable object and returns a pointer to it
!> @param ce the computing environment
!> @param params the input parameter list controlling the generation of the table
!> @param func the procedure to be tabulated; must have an interface matching @c tabulatedFunction1D
!> @returns table pointer to the initialized function table
!>
FUNCTION generateFunctionTable1D(ce,params,func) RESULT(table)
CLASS(FutilityComputingEnvironment),TARGET,INTENT(INOUT) :: ce
CLASS(ParamType),INTENT(IN) :: params
PROCEDURE(tabulatedFunction1D) :: func
CLASS(FunctionTable),POINTER :: table
!
REAL(SRK) :: maxRelError,maxRelErrorData,maxAbsError,maxAbsErrorData
REQUIRE(params%has('spacing') /= (params%has('maxRelError') .OR. params%has('maxAbsError')))
ALLOCATE(table)
CALL table%init(ce,params,func)
!Now setup the table. If spacing is specified, just call setup once for that spacing
IF(params%has('spacing')) THEN
CALL params%get('spacing',table%spacing)
REQUIRE(table%spacing > ZERO)
CALL generateTableData(table)
!If error is specified, start with 1000 points and refine until the error is small enough
ELSE
IF(params%has('maxRelError')) THEN
CALL params%get('maxRelError',maxRelError)
REQUIRE(maxRelError > ZERO)
ELSE
maxRelError=HUGE(maxRelError)
ENDIF
IF(params%has('maxAbsError')) THEN
CALL params%get('maxAbsError',maxAbsError)
REQUIRE(maxAbsError > ZERO)
ELSE
maxAbsError=HUGE(maxAbsError)
ENDIF
table%spacing = (table%maxRange - table%minRange)*0.001_SRK
maxRelErrorData = HUGE(maxRelError)
maxAbsErrorData = HUGE(maxAbsError)
DO WHILE(maxRelErrorData > maxRelError .OR. maxAbsErrorData > maxAbsError)
CALL generateTableData(table)
CALL evaluateTableQuality(table,maxRelErrorData,maxAbsErrorData)
ENDDO
ENDIF
ENDFUNCTION generateFunctionTable1D
!
!-------------------------------------------------------------------------------
!> @brief Initializes a function table without populating its data
!> @param this the @c FunctionTable to initialize
!> @param ce the computing environment
!> @param params the input parameter list controlling the initialization of the table
!> @param func the procedure to be tabulated; must have an interface matching @c tabulatedFunction1D
!>
SUBROUTINE initFunctionTable1D(this,ce,params,func)
CLASS(FunctionTable),INTENT(INOUT) :: this
CLASS(FutilityComputingEnvironment),TARGET,INTENT(INOUT) :: ce
CLASS(ParamType),INTENT(IN) :: params
PROCEDURE(tabulatedFunction1D) :: func
REQUIRE(params%has('minRange'))
REQUIRE(params%has('maxRange'))
REQUIRE(params%has('boundsErrorHigh'))
REQUIRE(params%has('boundsErrorLow'))
!Set the boundaries and default values (or error behavior) outside the boundaries
CALL params%get('boundsErrorHigh',this%boundsErrorHigh)
CALL params%get('boundsErrorLow',this%boundsErrorLow)
IF(.NOT.this%boundsErrorHigh) THEN
IF(params%has('highDefaultValue')) THEN
CALL params%get('highDefaultValue',this%highDefaultValue)
ENDIF
ENDIF
IF(.NOT.this%boundsErrorLow) THEN
IF(params%has('lowDefaultValue')) THEN
CALL params%get('lowDefaultValue',this%lowDefaultValue)
ENDIF
ENDIF
CALL params%get('minRange',this%minRange)
CALL params%get('maxRange',this%maxRange)
REQUIRE(this%minRange < this%maxRange)
!Associate pointers
this%func => func
this%ce => ce
this%isInit=.TRUE.
this%hasData=.FALSE.
ENDSUBROUTINE initFunctionTable1D
!
!-------------------------------------------------------------------------------
!> @brief Clears a @c FunctionTable object
!> @param this the table to clear
!>
SUBROUTINE clearFunctionTable1D(this)
CLASS(FunctionTable),INTENT(INOUT) :: this
this%isInit = .FALSE.
this%hasData = .FALSE.
this%boundsErrorHigh = .TRUE.
this%boundsErrorLow = .TRUE.
this%nPoints = 0_SIK
this%minRange = HUGE(ZERO)
this%maxRange = -HUGE(ZERO)
this%highDefaultValue = ZERO
this%lowDefaultValue = ZERO
this%spacing = -HUGE(ZERO)
this%inverseSpacing = -HUGE(ZERO)
IF(ALLOCATED(this%funcCoordinates)) DEALLOCATE(this%funcCoordinates)
IF(ALLOCATED(this%funcValues)) DEALLOCATE(this%funcValues)
this%func => NULL()
this%ce => NULL()
ENDSUBROUTINE clearFunctionTable1D
!
!-------------------------------------------------------------------------------
!> @brief Approximately evaluates the tabulated function at @c x
!> @param this the @c FunctionTable object
!> @param x the input value for the function
!> @returns y the approximate function values at @c x
!>
FUNCTION evaluateFunctionTable1D(this,x) RESULT(y)
CLASS(FunctionTable),INTENT(IN) :: this
REAL(SRK),INTENT(IN) :: x
REAL(SRK) :: y
!
CHARACTER(LEN=*),PARAMETER :: myName='evaluateFunctionTable1D'
INTEGER :: tableIndex
REAL(SRK) :: interval, fraction
REQUIRE(this%isInit)
REQUIRE(this%hasData)
REQUIRE(this%inverseSpacing > ZERO)
REQUIRE(ALLOCATED(this%funcCoordinates))
REQUIRE(ALLOCATED(this%funcValues))
interval = x - this%minRange
tableIndex = FLOOR(interval * this%inverseSpacing) + 1
IF(tableIndex < 1) THEN
IF(this%boundsErrorLow) THEN
CALL this%ce%exceptHandler%raiseError(modName//'::'//myName// &
' - Value '//str(x)//' is below the minimum table value '//str(this%minRange)//'!')
ELSE
y = this%lowDefaultValue
ENDIF
ELSEIF(tableIndex >= this%nPoints) THEN !Use >= to avoid index out of bounds in ELSE statement
IF(this%boundsErrorHigh) THEN
CALL this%ce%exceptHandler%raiseError(modName//'::'//myName// &
' - Value '//str(x)//' is above the maximum table value '//str(this%maxRange)//'!')
ELSE
y = this%highDefaultValue
ENDIF
ELSE
fraction = (x - this%funcCoordinates(tableIndex)) * this%inverseSpacing
y = (ONE - fraction) * this%funcValues(tableIndex) + fraction * this%funcValues(tableIndex+1)
ENDIF
ENDFUNCTION evaluateFunctionTable1D
!
!-------------------------------------------------------------------------------
!> @brief Generates the data for a @c FunctionTable object
!> @param table the object to populate data for
!>
!> If @c hasData is true, then it is assumed that the function table data is being
!> refined. The spacing will be cut in half and the data regenerated. If false, then
!> new data will be populated using whatever the current value of @c spacing is.
!>
SUBROUTINE generateTableData(table)
CLASS(FunctionTable),INTENT(INOUT) :: table
!
INTEGER(SIK) :: i,stride
REAL(SRK) :: x
REAL(SRK),ALLOCATABLE :: oldValues(:),oldCoordinates(:)
REQUIRE(ASSOCIATED(table%func))
REQUIRE(table%maxRange > -HUGE(table%maxRange))
REQUIRE(table%minRange < HUGE(table%minRange))
REQUIRE(table%spacing > ZERO)
IF(table%hasData) THEN
!Assume we've already generated the table once and are refining by 2x
IF((SIZE(table%funcValues) > 2) .AND. (MOD(SIZE(table%funcValues),2) == 1)) THEN
CALL MOVE_ALLOC(table%funcValues,oldValues)
CALL MOVE_ALLOC(table%funcCoordinates,oldCoordinates)
stride = 2
table%spacing = table%spacing * HALF
!Generated some table that doesn't conform to how we're going to generate it this time,
! so just throw out the old values
ELSE
DEALLOCATE(table%funcValues)
DEALLOCATE(table%funcCoordinates)
stride = 1
ENDIF
ELSE
stride = 1
ENDIF
table%inverseSpacing = ONE / table%spacing
table%nPoints = NINT((table%maxRange - table%minRange) * table%inverseSpacing) + 1
ALLOCATE(table%funcValues(table%nPoints))
ALLOCATE(table%funcCoordinates(table%nPoints))
IF(stride == 2) THEN
table%funcValues(1:table%nPoints:2)=oldValues(:)
table%funcCoordinates(1:table%nPoints:2)=oldCoordinates(:)
DEALLOCATE(oldValues)
DEALLOCATE(oldCoordinates)
ENDIF
DO i=stride,table%nPoints,stride
x = table%minRange + REAL(i-1,SRK)*table%spacing
table%funcCoordinates(i) = x
table%funcValues(i) = table%func(x)
ENDDO !i
table%hasData=.TRUE.
ENDSUBROUTINE generateTableData
!
!-------------------------------------------------------------------------------
!> @brief Evaluates the quality of a @c FunctionTable compared to the real function
!> @param table the @c FunctionTable to evaluate
!> @returns maxRelError the maximum relative error of the function table
!> @returns maxAbsError the maximum absolute error of the function table
!>
!> If the function returns 0.0 within double precision tolerance, then the value
!> of the table at that input is used to calculate the relative error. This may
!> be of importance when generating the table dynamically.
!>
SUBROUTINE evaluateTableQuality(table,maxRelError,maxAbsError)
CLASS(FunctionTable),INTENT(IN) :: table
REAL(SRK),INTENT(OUT) :: maxRelError
REAL(SRK),INTENT(OUT) :: maxAbsError
!
INTEGER(SIK) :: i
REAL(SRK) :: x,yTab,yRef
REQUIRE(ALLOCATED(table%funcValues))
REQUIRE(ASSOCIATED(table%func))
REQUIRE(table%maxRange > -HUGE(table%maxRange))
REQUIRE(table%minRange < HUGE(table%minRange))
REQUIRE(table%spacing > ZERO)
maxRelError = ZERO
maxAbsError = ZERO
DO i=1,table%nPoints-1
x = table%minRange + (REAL(i-1,SRK)+HALF) * table%spacing
yTab = table%evaluate(x)
yRef = table%func(x)
IF((yRef .APPROXEQA. ZERO) .AND. (.NOT.(yTab .APPROXEQA. ZERO))) THEN
maxRelError = MAX(maxRelError,yTab)
ELSE
maxRelError = MAX(maxRelError,ABS((yTab - yRef)/yRef))
ENDIF
maxAbsError = MAX(maxAbsError,ABS(yTab - yRef))
ENDDO
ENDSUBROUTINE evaluateTableQuality
!
ENDMODULE FunctionTableGeneratorModule
\ No newline at end of file
......@@ -76,6 +76,7 @@ SET(UNIT_TEST_NAMES
testSchemaParser
testSpeciesElements
testRSORprecon
testFunctionTableGenerator
)
IF("${PROJECT_NAME}" STREQUAL "Futility" OR "${PROJECT_NAME}" STREQUAL "MPACT")
......
......@@ -23,6 +23,7 @@ REGISTER_SUBTEST('GCD',testGCD)
REGISTER_SUBTEST('GCD',testLCM)
REGISTER_SUBTEST('ATAN2PI',testATAN2PI)
REGISTER_SUBTEST('RotateQtrClockwise',testRotateQtrClockwise)
REGISTER_SUBTEST("testThirdOrderBickley", testThirdOrderBickley)
FINALIZE_TEST()
!
......@@ -247,4 +248,72 @@ FUNCTION oldATAN2PI(x,y) RESULT(theta)
IF(theta < 0) theta=2.0_SRK*PI+theta
ENDFUNCTION oldATAN2PI
!
!-------------------------------------------------------------------------------
SUBROUTINE testThirdOrderBickley()
ASSERT(SOFTEQ(ThirdOrderBickley(0.025_SRK),0.760874665500000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.025)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.075_SRK),0.714497185937500_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.075)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.125_SRK),0.671358382812500_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.125)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.175_SRK),0.631150814500000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.175)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.225_SRK),0.593618806687500_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.225)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.275_SRK),0.558542169500000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.275)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.325_SRK),0.525727645750000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.325)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.375_SRK),0.495003064062500_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.375)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.425_SRK),0.466214163562500_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.425)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.475_SRK),0.439221359750000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.475)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.550_SRK),0.401825452250000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.550)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.650_SRK),0.357156291500000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.650)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.750_SRK),0.317712862500000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.750)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.850_SRK),0.282831692750000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.850)")
ASSERT(SOFTEQ(ThirdOrderBickley(0.950_SRK),0.251945412000000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(0.950)")
ASSERT(SOFTEQ(ThirdOrderBickley(1.200_SRK),0.189159171200000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(1.200)")
ASSERT(SOFTEQ(ThirdOrderBickley(1.600_SRK),0.120309080320000_SRK ,1.0e-12_SRK),"ThirdOrderBickley(1.600)")
ASSERT(SOFTEQ(ThirdOrderBickley(2.000_SRK),7.696245999999995E-002_SRK,1.0e-12_SRK),"ThirdOrderBickley(2.000)")
ASSERT(SOFTEQ(ThirdOrderBickley(2.400_SRK),4.945647152000016E-002_SRK,1.0e-12_SRK),"ThirdOrderBickley(2.400)")
ASSERT(SOFTEQ(ThirdOrderBickley(2.800_SRK),3.189697312000006E-002_SRK,1.0e-12_SRK),"ThirdOrderBickley(2.800)")
ASSERT(SOFTEQ(ThirdOrderBickley(3.200_SRK),2.063440492800001E-002_SRK,1.0e-12_SRK),"ThirdOrderBickley(3.200)")
ASSERT(SOFTEQ(ThirdOrderBickley(3.600_SRK),1.338288511999999E-002_SRK,1.0e-12_SRK),"ThirdOrderBickley(3.600)")
ASSERT(SOFTEQ(ThirdOrderBickley(4.000_SRK),8.698716000000009E-003_SRK,1.0e-12_SRK),"ThirdOrderBickley(4.000)")
ASSERT(SOFTEQ(ThirdOrderBickley(4.400_SRK),5.664898399999979E-003_SRK,1.0e-12_SRK),"ThirdOrderBickley(4.400)")
ASSERT(SOFTEQ(ThirdOrderBickley(4.800_SRK),3.695205679999994E-003_SRK,1.0e-12_SRK),"ThirdOrderBickley(4.800)")
ASSERT(SOFTEQ(ThirdOrderBickley(5.400_SRK),1.951856920000009E-003_SRK,1.0e-12_SRK),"ThirdOrderBickley(2.4)")
ASSERT(SOFTEQ(ThirdOrderBickley(6.200_SRK),8.371110279999996E-004_SRK,1.0e-12_SRK),"ThirdOrderBickley(2.8)")
ASSERT(SOFTEQ(ThirdOrderBickley(7.000_SRK),3.604257000000000E-004_SRK,1.0e-12_SRK),"ThirdOrderBickley(3.2)")
ASSERT(SOFTEQ(ThirdOrderBickley(7.800_SRK),1.557452960000002E-004_SRK,1.0e-12_SRK),"ThirdOrderBickley(3.6)")
ASSERT(SOFTEQ(ThirdOrderBickley(8.600_SRK),6.751333079999975E-005_SRK,1.0e-12_SRK),"ThirdOrderBickley(4.0)")
!data from Wolfram Alpha 76 significant figures
!arg x ThirdOrderBickley(x) result of the Bickley function of order three
!0.025 0.76087435435473089481415940877597033571853054821901991516400325474643284773336
!0.075 0.71449679782598526588821547470452465456072719612047630320980195234993342864256
!0.125 0.67135807577109342817946318906174187092787964671445454738446543799021539000978
!0.175 0.63115047862169504452063910448507825276853540965153164809546500702205218310449
!0.225 0.59361841911938945552648920262348309470941047039495859963279516413794004545305
!0.275 0.55854178728401589214049334376515267256572163215350210072437632033519341209916
!0.325 0.52572724070349487181924281894203869507925667656046621747294445654651987447062
!0.375 0.49500276632364780191816272471601602357514824260519750266189586060847625690883
!0.425 0.46621393979863575392216528721577559310239218091302181577479117780150980744750
!0.475 0.43922118686469524800396457044600487137488630880560313204930013463782482792567
!0.550 0.40182535282391698297458635029025432128965486542492333850466147236063725415729
!0.650 0.35715627270140456636611676768402689893215759424244353827223333051537222578432
!0.750 0.31771277656952221958158362255061189690906158712914606465171107896701959742579
!0.850 0.28283162553269040885783128737810736981905570575929112100383119692506037271062
!0.950 0.25194540869425338958124027964104324074752288880999752520768477667293821782424
!1.200 0.18916287763245597978706605859244691850573157613366372399064946356597412542183
!1.600 0.12031089184783858071717638522312464645541380493830736933748211196063082301064
!2.000 0.076963590311658422876818821208590636542634695527976466961577389725761406511519
!2.400 0.049457028031707628064213758059820561481339021775308114578972671841384222775386
!2.800 0.031897511889064199646903466297702116064408073380184568612045684723723587333865
!3.200 0.020634782845562449157993465589641699827465512521288053099219320252709113760499
!3.600 0.013382991329065580861327812619087558102372841626198681240934154676282966267723
!4.000 0.0086987886861997011003542329497674945951430192610029923790805463110000398579947
!4.400 0.0056648909039604982498722037905876778734163941370103855884253919179207346896954
!4.800 0.0036953063069731639358811565454225299312839796120822391929484878605617712940590
!5.400 0.0019521925233853647800369181229233195067620714546474160712559007214062890525572
!6.200 0.00083732548774562206899852981353429892451414336783756347601246685001654830130831