!! Module for managing plain text tables
!|author: Giovanni Ravazzani
! license: GPL
!
!### History
!
! current version 1.6 - 24th April 2025
!
! | version | date | comment |
! |----------|-------------|----------|
! | 1.0 | 1/Oct/2008 | Original code |
! | 1.1 | 4/Jan/2011 | Read table specifing an id |
! | 1.2 | 8/Apr/2023 | comments reformatted to adhere FORD specs. |
! | 1.3 | 17/Apr/2024 | new public function TablesGetIds |
! | 1.4 | 30/Aug/2024 | routines to create a new table from scratch and populate it |
! | 1.5 | 04/Sep/2024 | new subroutine TablesGetFloatByString |
! | 1.6 | 24/Apr/2025 | LINELENGTH set to 500 to prevent stack overflow in lon tables |
!
!
!### License
! license: GNU GPL
!
! This file is part of
!
! MOSAICO -- MOdular library for raSter bAsed hydrologIcal appliCatiOn.
!
! Copyright (C) 2011 Giovanni Ravazzani
!
!### Code Description
! Language: Fortran 90.
!
! Software Standards: "European Standards for Writing and
! Documenting Exchangeable Fortran 90 Code".
!
!### Module Description:
!
! Module for managing tables
! Tables are stored in plain text.
! One single file may contain more than one table.
! Example of a table:
!
!```
! Table Start
! Title: Stage discharge relatioship. #inline comment
! Id: Tab01 # mandatory
!
! # This is a sample comment. You can put anything you want
! # on comment lines. Comment lines can be put everywhere
!
! Columns: [Stage] [Discharge] [Method]
! Units: [m a.s.l.] [m3/s] [-]
! 300.0 10.0 measure
! 300.5 20.0 measure
! 301.0 50.0 measure
! 301.5 100.0 extrapolation
! 302.0 200.0 extrapolation
! 302.5 500.0 extrapolation
! # another comment
! Table End
!```
!
! Example program, reading tables from files:
!
!```fortran
! PROGRAM TestTableLib
!
! USE TableLib
! USE DataTypeSizes
!
! TYPE (TableCollection) :: tables
! TYPE (Table) :: tab, tab2
! REAL (KIND = float) :: out
! REAL (KIND = double) :: dOut
! INTEGER :: i
!
! !initialize a new table reading from the file passed as an argument.
! !At the end of initialization, file is closed.
! CALL TableNew ( 'table.txt', tab )
!
! !initialize a new table reading from a file already open.
! !Unit of the file is passed as an argument.
! !At the end of initialization, file is not closed.
! OPEN (unit=10, file='table2.txt')
! CALL TableNew ( 10, tab2 ) .
!
! !initialize a collection of tables reading from a file whose name
! !is specified as argument.
! CALL TableNew ( 'tables.txt', tables )
!
! !extract value from table with different methods.
! !input value is a float number. Output can be long integer,
! !float real or double real.
!
! !get float that corresponds exactly to input float. Bound is not
! !necessary (it does not make sense).
! !If input value is not found, an error message is logged.
! CALL TableGetValue ( 302., tab, 'stage', 'discharge', 'exact', out)
! WRITE(*,*) 'The discharge corresponding to selected case is: ', out
!
! !get float calculating a linear interpolation between the two nearest values.
! !Option bound = 'fixed' limits the search inside the extreme values
! !of the table. If extreme values are exceeded, an error is thrown.
! CALL TableGetValue ( 302.4, tab, 'stage', 'discharge', 'linear', out, &
! bound = 'fixed' )
! WRITE(*,*) 'The discharge corresponding to input value is: ', out
!
! !get float calculating a linear interpolation between the two nearest values.
! !Option bound = 'extendlinear' means that if the input value is outside
! !extreme values of the table, they are linearly extended using the
! !last two elements of the table. A warning message is logged.
! CALL TableGetValue ( 304., tab, 'stage', 'discharge', 'linear', out, &
! bound = 'extendlinear' )
! WRITE(*,*) 'The discharge corresponding to input value is: ', out
!
! !get float calculating a linear interpolation between the two nearest values.
! !Option bound = 'extendconstant' means that if the input value is
! !outside extreme values of the table, the last element is extended
! !as a constant. A warning message is logged.
! CALL TableGetValue ( 304., tab, 'stage', 'discharge', 'linear', out, &
! bound = 'extendconstant' )
! WRITE(*,*) 'The discharge corresponding to input value is: ', out
!
! !get float searching for the nearest value.
! CALL TableGetValue ( 302.55, tab, 'stage', 'discharge', 'nearest', out)
! WRITE(*,*) 'The discharge nearest to input value is: ', out
!
! !get double calculating a linear interpolation between the two nearest values.
! !Option bound = 'fixed' limits the search inside the extreme values of the
! !table. If extreme values are exceeded, an error is thrown.
! CALL TableGetValue ( 302.4, tab, 'stage', 'discharge', 'linear', dOut, &
! bound = 'fixed' )
! WRITE(*,*) 'The discharge corresponding to input value is: ', dOut
!
! !get float calculating a linear interpolation between the two nearest values.
! !Option bound = 'fixed' limits the search inside the extreme values of
! !the table. If extreme values are exceeded, an error is thrown.
! CALL TableGetValue ( 302.4, tables, 'tab02', 'stage', 'discharge', 'linear', &
! out, bound = 'fixed' )
! WRITE(*,*) 'The discharge corresponding to input value is: ', out
!
! !get double calculating a linear interpolation between the two nearest values.
! ! Option bound = 'fixed' limits the search inside the extreme values of the table.
! !If extreme values are exceeded, an error is thrown.
! CALL TableGetValue ( 302.4, tables, 'tab02', 'stage', 'discharge', 'linear', &
! dOut, bound = 'fixed' )
! WRITE(*,*) 'The discharge corresponding to input value is: ', dOut
!
! !export table on file. Name of the file is passed as argument
! CALL TableExport ( tab, 'fileout.txt' )
!
! !export table on a file taht is already open. Unit of file is passed as argument
! OPEN (UNIT = 20, file = 'exported_table.txt')
! CALL TableExport ( tab, 20 )
! CLOSE (20)
!
! !export a collection of tables on a file. Name of the file is passed as argument
! CALL TableExport ( tables, 'table_collections.txt' )
!
! !export just one table from a collection of tables on a filetaht is already open.
! !Unit of file is passed as argument
! OPEN (UNIT = 20, file = 'tab02.txt')
! CALL TableExport ( tables, 20, 'tab02' )
! CLOSE (20)
!
!END PROGRAM TestTableLib
!
!```
!
! Example program, creating table from scratch:
!
!```fortran
! PROGRAM CreateTableFromScratch
!
! USE TableLib
! USE DataTypeSizes
!
! TYPE (Table) :: tab
! CHARACTER (LEN = 300) :: string
! CHARACTER (LEN = 100) :: row (3)
!
! !create new table
! CALL TableNew ( tab )
!
! !set table id
! string = 'table id'
! CALL TableSetId ( tab, string)
!
! !set table title
! string = 'table title'
! CALL TableSetTitle ( tab, string)
!
! !Allocate variables
! CALL TableSetRowCol ( tab, 2, 3 )
!
! !set column header and unit
! CALL TableSetColHeader (tab, 1, 'id')
! CALL TableSetColHeader (tab, 2, 'Qin')
! CALL TableSetColHeader (tab, 3, 'Qout')
!
! CALL TableSetColUnit (tab, 1, '-')
! CALL TableSetColUnit (tab, 2, 'm3/s')
! CALL TableSetColUnit (tab, 3, 'm3/s')
!
! !fill in the first row
! row = (/ '1', '3.5', '2.0' /)
! CALL TableFillRow (tab, 1, row)
!
! !fill in the second row
! row = (/ '2', '13.5', '15.4' /)
! CALL TableFillRow (tab, 2, row)
!
! END PROGRAM CreateTableFromScratch
!
!```
! References and Credits:
! ODT data table format http://math.nist.gov/oommf/doc/userguide11b2/userguide/Data_table_format_ODT.html
!
! Known issues:
! when processing table with lots of rows, stack overflow may occur.
! Decrease LINELENGTH parameter or increase stack size before compiling.
!
MODULE TableLib
!
! Code Description:
! Language: Fortran 90.
! Software Standards: "European Standards for Writing and
! Documenting Exchangeable Fortran 90 Code".
! Declarations:
! Modules used:
USE DataTypeSizes, ONLY : &
!Imported parameters:
short, long, float, double
USE LogLib, ONLY : &
! Imported Routines:
Catch
USE Utilities, ONLY : &
! Imported Routines:
LinearInterp
USE FileSys, ONLY : &
!imported routines:
FileSyncToLastLine
IMPLICIT NONE
! Global (i.e. public) declarations:
!Global type definitions:
TYPE Table
CHARACTER ( LEN = 300 ) :: title !! Max length of title: 300 characters
CHARACTER ( LEN = 30 ) :: id !! Max length of id: 30 characters
INTEGER ( KIND = long ) :: noRows !! number of rows
INTEGER ( KIND = long ) :: noCols !! number of columns
TYPE (Column), POINTER :: col (:)
END TYPE Table
TYPE TableCollection
INTEGER (KIND = long) :: number
TYPE (Table), POINTER :: elem (:)
END TYPE TableCollection
! Global procedures:
PUBLIC :: TableNew
PUBLIC :: TableGetValue
PUBLIC :: TableExport
PUBLIC :: TableGetNrows
PUBLIC :: TablesGetIds
PUBLIC :: TableSetId
PUBLIC :: TableSetTitle
PUBLIC :: TableSetRowCol
PUBLIC :: TableSetColHeader
PUBLIC :: TableSetColUnit
PUBLIC :: TableFillRow
! Local (i.e. private) Declarations:
! Local Procedures:
PRIVATE :: TableFileSync
PRIVATE :: TableSyncById
PRIVATE :: TableReadTitle
PRIVATE :: TableReadId
PRIVATE :: TablesGetFromFile
PRIVATE :: TablesGetFromUnit
PRIVATE :: TableGetFromFile
PRIVATE :: TableGetFromUnit
PRIVATE :: TableStoreLines
PRIVATE :: TableCountCols
PRIVATE :: TableCountRows
PRIVATE :: CheckId
PRIVATE :: TableReadHeader
PRIVATE :: TableReadUnit
PRIVATE :: TableReadContent
PRIVATE :: TableGetFloat
PRIVATE :: TableGetFloatByString
PRIVATE :: TableGetString
PRIVATE :: TableGetDouble
PRIVATE :: TablesGetFloat
PRIVATE :: TablesGetDouble
PRIVATE :: TablesGetString
PRIVATE :: TableWriteToFile
PRIVATE :: TableWriteToUnit
PRIVATE :: TablesWriteToFile
PRIVATE :: TablesWriteToUnit
PRIVATE :: TableGetNumberRows
PRIVATE :: TableCreate
! Local Type Definitions:
TYPE Column
CHARACTER (LEN = 100) :: header
CHARACTER (LEN = 100) :: unit
CHARACTER (LEN = 100), POINTER :: row (:)
END TYPE Column
PRIVATE :: Column
! Local Parameters:
INTEGER (KIND = long), PRIVATE, PARAMETER :: LINELENGTH = 500
! Operator definitions:
! Define new operators or overload existing ones.
INTERFACE TableNew
MODULE PROCEDURE TablesGetFromFile
MODULE PROCEDURE TablesGetFromUnit
MODULE PROCEDURE TableGetFromFile
MODULE PROCEDURE TableGetFromUnit
MODULE PROCEDURE TableCreate
END INTERFACE
INTERFACE TableGetValue
MODULE PROCEDURE TableGetFloat
MODULE PROCEDURE TableGetFloatByString
MODULE PROCEDURE TablesGetFloatByString
MODULE PROCEDURE TableGetDouble
MODULE PROCEDURE TableGetString
MODULE PROCEDURE TablesGetFloat
MODULE PROCEDURE TablesGetDouble
MODULE PROCEDURE TablesGetString
END INTERFACE
INTERFACE TableExport
MODULE PROCEDURE TableWriteToFile
MODULE PROCEDURE TableWriteToUnit
MODULE PROCEDURE TablesWriteToFile
MODULE PROCEDURE TablesWriteToUnit
END INTERFACE
INTERFACE TableGetNrows
MODULE PROCEDURE TableGetNumberRows
MODULE PROCEDURE TablesGetNumberRows
END INTERFACE
!=======
CONTAINS
!=======
! Define procedures contained in this module.
!==============================================================================
!| Description:
! write a table on file.
! Arguments:
! ` tab` table to export
! `file` file to whom write the table
!
SUBROUTINE TableWriteToFile &
( tab, file, append )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: file
LOGICAL, OPTIONAL, INTENT (IN) :: append
! Type defined arguments with intent (in):
TYPE (Table), INTENT (IN) :: tab
!Local variables:
INTEGER (KIND = short) :: iunit
INTEGER (KIND = long) :: i, j
LOGICAL :: fileExists
!------------end of declaration------------------------------------------------
!if append option is present, file already exists
fileExists = .FALSE.
IF ( PRESENT (append) ) THEN
IF (append) THEN
fileExists = .TRUE.
END IF
END IF
!get a free fortran unit
iunit = GetUnit ()
IF (fileExists) THEN
OPEN (UNIT = iunit, FILE = file, STATUS = "old")
!sync to the last line
CALL FileSyncToLastLine ( iunit, 2 )
ELSE
OPEN (UNIT = iunit, FILE = file, STATUS = "unknown")
END IF
!write keyword for start table
WRITE(iunit,'(a)') 'Table Start'
!write title if exists
IF ( StringCompact ( tab % title ) /= '' ) THEN
WRITE(iunit,'(a7)', ADVANCE = 'no') 'Title: '
WRITE(iunit,'(a)') TRIM (tab % title)
END IF
!write id
WRITE(iunit,'(a4)', ADVANCE = 'no') 'Id: '
WRITE(iunit,'(a)') TRIM (tab % id)
!write column headers
WRITE(iunit,'(a9)', ADVANCE = 'no') 'Columns: '
DO i = 1, tab % noCols - 1
WRITE(iunit,'(a1,a,a2)', ADVANCE = 'no') '[', TRIM (tab % col (i) % header ), '] '
END DO
WRITE(iunit,'(a1,a,a1)') '[', TRIM (tab % col (tab % noCols) % header ), ']'
!write column unit
WRITE(iunit,'(a7)', ADVANCE = 'no') 'Units: '
DO i = 1, tab % noCols - 1
WRITE(iunit,'(a1,a,a2)', ADVANCE = 'no') '[', TRIM (tab % col (i) % unit ), '] '
END DO
WRITE(iunit,'(a1,a,a1)') '[', TRIM (tab % col (tab % noCols) % unit ), ']'
!write content
DO i = 1, tab % noRows
DO j = 1, tab % noCols - 1
WRITE(iunit,'(a)', ADVANCE = 'no') TRIM (tab % col (j) % row (i) ) // ' '
END DO
WRITE(iunit,'(a)') TRIM (tab % col (tab % noCols) % row (i) )
END DO
!write keyword for close table
WRITE(iunit,'(a)') 'Table End'
CLOSE (iunit)
END SUBROUTINE TableWriteToFile
!==============================================================================
!| Description:
! write a table on file taht is already open.
! Arguments:
! `tab` table to export
! `iunit` unit of file to whom write the table
!
SUBROUTINE TableWriteToUnit &
( tab, iunit )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: iunit
! Type defined arguments with intent (in):
TYPE (Table), INTENT (IN) :: tab
!Local variables:
INTEGER (KIND = long) :: i, j
!------------end of declaration------------------------------------------------
!write keyword for start table
WRITE(iunit,'(a)') 'Table Start'
!write title if exists
IF ( StringCompact ( tab % title ) /= '' ) THEN
WRITE(iunit,'(a7)', ADVANCE = 'no') 'Title: '
WRITE(iunit,'(a)') TRIM (tab % title)
END IF
!write id
WRITE(iunit,'(a4)', ADVANCE = 'no') 'Id: '
WRITE(iunit,'(a)') TRIM (tab % id)
!write column headers
WRITE(iunit,'(a9)', ADVANCE = 'no') 'Columns: '
DO i = 1, tab % noCols - 1
WRITE(iunit,'(a1,a,a2)', ADVANCE = 'no') '[', TRIM (tab % col (i) % header ), '] '
END DO
WRITE(iunit,'(a1,a,a1)') '[', TRIM (tab % col (tab % noCols) % header ), ']'
!write column unit
WRITE(iunit,'(a7)', ADVANCE = 'no') 'Units: '
DO i = 1, tab % noCols - 1
WRITE(iunit,'(a1,a,a2)', ADVANCE = 'no') '[', TRIM (tab % col (i) % unit ), '] '
END DO
WRITE(iunit,'(a1,a,a1)') '[', TRIM (tab % col (tab % noCols) % unit ), ']'
!write content
DO i = 1, tab % noRows
DO j = 1, tab % noCols - 1
WRITE(iunit,'(a)', ADVANCE = 'no') TRIM (tab % col (j) % row (i) ) // ' '
END DO
WRITE(iunit,'(a)') TRIM (tab % col (tab % noCols) % row (i) )
END DO
!write keyword for close table
WRITE(iunit,'(a)') 'Table End'
END SUBROUTINE TableWriteToUnit
!==============================================================================
!| Description:
! write a collection of tables on file. If id is present, only the table
! corresponding to that id is written.
! Arguments:
! `tables` collection of tables to be exported
! `file` file to whom write the table
SUBROUTINE TablesWriteToFile &
( tables, file, id )
USE Utilities, ONLY: &
!Imported routines:
GetUnit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: file
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: id
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
!Local variables:
INTEGER (KIND = short) :: iunit
INTEGER (KIND = long) :: i
!------------end of declaration------------------------------------------------
!get a free fortran unit
iunit = GetUnit ()
OPEN (UNIT = iunit, FILE = file, STATUS = "new")
IF ( PRESENT (id) ) THEN
CALL TableWriteToUnit ( tables % elem ( TableSyncById (tables, id) ), iunit )
ELSE
DO i = 1, tables % number
CALL TableWriteToUnit ( tables % elem (i), iunit )
WRITE(iunit,*)
END DO
END IF
CLOSE (iunit)
END SUBROUTINE TablesWriteToFile
!==============================================================================
!| Description:
! write a collection of tables on file already open. If id is present,
! only the table corresponding to that id is written.
! Arguments:
! `tables` collection of tables to be exported
! `iunit` unit to whom write the table
SUBROUTINE TablesWriteToUnit &
( tables, iunit, id )
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: iunit
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: id
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
!Local variables:
INTEGER (KIND = long) :: i
!------------end of declaration------------------------------------------------
IF ( PRESENT (id) ) THEN
CALL TableWriteToUnit ( tables % elem ( TableSyncById (tables, id) ), iunit )
ELSE
DO i = 1, tables % number
CALL TableWriteToUnit ( tables % elem (i), iunit )
WRITE(iunit,*)
END DO
END IF
END SUBROUTINE TablesWriteToUnit
!==============================================================================
!| Description:
! returns a float from column defined by keyOut corresponding to valueIn
! contained in column defined by keyIn.
! Arguments:
! `valueIn` input value
! `tab` table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
! `match` method to match input value. Possible values are:
! 'exact' = column must contain exact input value
! 'linear' = calculates linear interpolation between two
! bounding values
! 'nearest' = search for the nearest value in input column
! ` bound` method to manage bounds. Possible values are:
! 'fixed' = extreme values are treated as a wall
! 'extendlinear' = extend bounds with linear interpolation
! of last two extreme values
! 'extendconstant' = extend bounds preserving extreme value constant
SUBROUTINE TableGetFloat &
!
( valueIn, tab, keyIn, keyOut, match, valueOut, bound )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringToFloat, ToString
USE LogLib, ONLY : &
! Imported Routines:
Catch
USE ErrorCodes, ONLY : &
! Imported parameters:
unknownOption
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
CHARACTER (LEN = *), INTENT (IN) :: match
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: bound
! Type defined arguments with intent (in):
TYPE (Table), INTENT (IN) :: tab
! Scalar arguments with intent(out):
REAL (KIND = float), INTENT (OUT) :: valueOut
! Local scalars:
TYPE (Column), POINTER :: colIn
TYPE (Column), POINTER :: colOut
INTEGER (KIND = short) :: i
CHARACTER (LEN = 100) :: string
LOGICAL :: foundValue
REAL (KIND = float) :: upperIn
REAL (KIND = float) :: lowerIn
REAL (KIND = float) :: upperOut
REAL (KIND = float) :: lowerOut
REAL (KIND = float) :: bias
!------------end of declaration------------------------------------------------
!inizialization
foundValue = .FALSE.
!find columns to be processed
DO i = 1, tab % noCols
string = StringCompact (StringToUpper (tab % col (i) % header) )
IF ( string == StringToUpper(keyIn) ) THEN
colIn => tab % col (i) !colIn is an alias of the input column
ELSE IF ( string == StringToUpper(keyOut) ) THEN
colOut => tab % col (i) !colOut is an alias of the output column
END IF
END DO
SELECT CASE ( StringToUpper (match) )
CASE ('EXACT')
!bound method is not necessary, only fixed makes sense.
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) == valueIn ) THEN
foundValue = .TRUE.
valueOut = StringToFloat (colout % row (i))
END IF
END DO
IF ( .NOT. foundValue ) THEN
CALL Catch ('error', 'TableLib', &
TRIM ( ToString (valueIn) ) // ' not found in table: ' , &
argument = tab % id )
END IF
CASE ('LINEAR')
!if bound is not specified, assume FIXED
IF (.NOT. PRESENT (bound) ) THEN
IF ( StringToFloat (colIn % row (1)) > valueIn .OR. &
StringToFloat (colIn % row (tab % noRows) ) < valueIn ) THEN
CALL Catch ('error', 'TableLib', 'bounds exceeded in table: ', &
argument = TRIM(tab % Id) )
END IF
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToFloat ( colOut % row (i) )
upperOut = StringToFloat ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
ELSE
SELECT CASE ( StringToUpper (bound) )
CASE ('FIXED')
IF ( StringToFloat (colIn % row (1)) > valueIn .OR. &
StringToFloat (colIn % row (tab % noRows) ) < valueIn ) THEN
CALL Catch ('error', 'TableLib', 'bounds exceeded in table: ', &
argument = TRIM(tab % Id) )
END IF
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToFloat ( colOut % row (i) )
upperOut = StringToFloat ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
CASE ('EXTENDLINEAR')
!If value exceed lower bound
IF ( StringToFloat (colIn % row (1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (1) )
upperIn = StringToFloat ( colIn % row (2) )
lowerOut = StringToFloat ( colOut % row (1) )
upperOut = StringToFloat ( colOut % row (2) )
!calculate interpolation
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
CALL Catch ('warning', 'TableLib', &
'value is under lower bound: extending linearly')
!if value exceed upper bound
ELSE IF ( StringToFloat (colIn % row (tab % noRows)) <= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row ( tab % noRows - 1) )
upperIn = StringToFloat ( colIn % row ( tab % noRows ) )
lowerOut = StringToFloat ( colOut % row ( tab % noRows - 1) )
upperOut = StringToFloat ( colOut % row ( tab % noRows ) )
!calculate interpolation
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
CALL Catch ('warning', 'TableLib', &
'value is over upper bound: extending linearly')
ELSE !value is between the boundary of the table
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToFloat ( colOut % row (i) )
upperOut = StringToFloat ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
END IF
CASE ('EXTENDCONSTANT')
!If value exceed lower bound
IF ( StringToFloat (colIn % row (1)) >= valueIn ) THEN
valueOut = StringToFloat (colOut % row (1))
CALL Catch ('warning', 'TableLib', &
'value is under lower bound: extending constantly')
!if value exceed upper bound
ELSE IF ( StringToFloat (colIn % row (tab % noRows)) <= valueIn ) THEN
valueOut = StringToFloat (colOut % row (tab % noRows))
CALL Catch ('warning', 'TableLib', &
'value is over upper bound: extending constantly')
ELSE !value is between the boundary of the table
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToFloat ( colOut % row (i) )
upperOut = StringToFloat ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
END IF
CASE DEFAULT
CALL Catch ('error', 'TableLib', &
'unknown option in call to TableGetValue: ' , &
code = unknownOption, argument = TRIM(bound) )
END SELECT
END IF
CASE ('NEAREST')
!bound method is not necessary, only fixed makes sense.
bias = HUGE (bias) !initializa bias to biggest number
DO i = 1, tab % noRows
IF ( ABS ( StringToFloat (colIn % row (i)) - valueIn ) < bias ) THEN
bias = ABS ( StringToFloat (colIn % row (i)) - valueIn )
valueOut = StringToFloat (colOut % row (i))
END IF
END DO
CASE DEFAULT
CALL Catch ('error', 'TableLib', &
'unknown option in call to TableGetValue: ' , &
code = unknownOption, argument = TRIM(match) )
END SELECT
END SUBROUTINE TableGetFloat
!==============================================================================
!| Description:
! returns a float from column defined by keyOut corresponding to valueIn
! contained in column defined by keyIn. Table is identified by its id.
! Arguments:
! `valueIn` input value
! `tables` collection of tables to search in
! `id` id of the table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
! `match` method to match input value. Possible values are:
! 'exact' = column must contain exact input value
! 'linear' = calculates linear interpolation between two
! bounding values
! 'nearest' = search for the nearest value in input column
! `bound` method to manage bounds. Possible values are:
! 'fixed' = extreme values are treated as a wall
! 'extendlinear' = extend bounds with linear interpolation
! of last two extreme values
! 'extendconstant' = extend bounds preserving extreme value constant
SUBROUTINE TablesGetFloat &
!
( valueIn, tables, id, keyIn, keyOut, match, valueOut, bound )
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: id
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
CHARACTER (LEN = *), INTENT (IN) :: match
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: bound
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (OUT) :: valueOut
!------------end of declaration------------------------------------------------
IF ( PRESENT (bound) ) THEN
CALL TableGetFloat ( valueIn, tables % elem ( TableSyncById (tables, id) ), &
keyIn, keyOut, match, valueOut, bound )
ELSE
CALL TableGetFloat ( valueIn, tables % elem ( TableSyncById (tables, id) ), &
keyIn, keyOut, match, valueOut)
END IF
END SUBROUTINE TablesGetFloat
!==============================================================================
!| Description:
! returns a float from column defined by keyOut corresponding to valueIn
! (string) contained in column defined by keyIn. Table is identified by its id.
! Arguments:
! `valueIn` input value
! `tables` collection of tables to search in
! `id` id of the table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
!
! The method to match input value is 'exact' by definition,
! no need to include optional arguments
!
SUBROUTINE TablesGetFloatByString &
!
( valueIn, tables, id, keyIn, keyOut, valueOut )
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: id
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (OUT) :: valueOut
!------------end of declaration------------------------------------------------
CALL TableGetFloatByString ( valueIn, tables % elem ( TableSyncById (tables, id) ), &
keyIn, keyOut, valueOut )
END SUBROUTINE TablesGetFloatByString
!==============================================================================
!| Description:
! returns a float from column defined by keyOut corresponding to
! valueIn (string) contained in column defined by keyIn.
! Arguments:
! `valueIn` input value
! `tab` table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
!
! The method to match input value is 'exact' by definition,
! no need to include optional arguments
!
SUBROUTINE TableGetFloatByString &
!
( valueIn, tab, keyIn, keyOut, valueOut )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringToFloat, ToString
USE LogLib, ONLY : &
! Imported Routines:
Catch
USE ErrorCodes, ONLY : &
! Imported parameters:
unknownOption
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
! Type defined arguments with intent (in):
TYPE (Table), INTENT (IN) :: tab
! Scalar arguments with intent(out):
REAL (KIND = float), INTENT (OUT) :: valueOut
! Local scalars:
TYPE (Column), POINTER :: colIn
TYPE (Column), POINTER :: colOut
INTEGER (KIND = short) :: i
CHARACTER (LEN = 100) :: string
CHARACTER (LEN = 100) :: string2
LOGICAL :: foundValue
!------------end of declaration------------------------------------------------
!inizialization
foundValue = .FALSE.
!find columns to be processed
DO i = 1, tab % noCols
string = StringCompact (StringToUpper (tab % col (i) % header) )
IF ( string == StringToUpper(keyIn) ) THEN
colIn => tab % col (i) !colIn is an alias of the input column
ELSE IF ( string == StringToUpper(keyOut) ) THEN
colOut => tab % col (i) !colOut is an alias of the output column
END IF
END DO
!find match for valueIn
string = StringToUpper ( valueIn )
DO i = 1, tab % noRows
string2 = StringToUpper ( colIn % row (i) )
IF ( string == string2 ) THEN
foundValue = .TRUE.
valueOut = StringToFloat (colout % row (i))
END IF
END DO
IF ( .NOT. foundValue ) THEN
CALL Catch ('error', 'TableLib', &
TRIM ( TRIM(valueIn) ) // ' not found in table: ' , &
argument = tab % id )
END IF
RETURN
END SUBROUTINE TableGetFloatByString
!==============================================================================
!| Description:
! returns a double from column defined by keyOut corresponding to valueIn
! contained in column defined by keyIn.
! Arguments:
! `valueIn` input value
! `tab` table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
! `match` method to match input value. Possible values are:
! 'exact' = column must contain exact input value
! 'linear' = calculates linear interpolation between two
! bounding values
! 'nearest' = search for the nearest value in input column
! `bound` method to manage bounds. Possible values are:
! 'fixed' = extreme values are treated as a wall
! 'extendlinear' = extend bounds with linear interpolation
! of last two extreme values
! 'extendconstant' = extend bounds preserving extreme value constant
SUBROUTINE TableGetDouble &
!
( valueIn, tab, keyIn, keyOut, match, valueOut, bound )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringToFloat, ToString, &
StringToDouble
USE LogLib, ONLY : &
! Imported Routines:
Catch
USE ErrorCodes, ONLY : &
! Imported parameters:
unknownOption
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
CHARACTER (LEN = *), INTENT (IN) :: match
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: bound
! Type defined arguments with intent (in):
TYPE (Table), INTENT (IN) :: tab
! Scalar arguments with intent(in):
REAL (KIND = double), INTENT (OUT) :: valueOut
! Local scalars:
TYPE (Column), POINTER :: colIn
TYPE (Column), POINTER :: colOut
INTEGER (KIND = short) :: i
CHARACTER (LEN = 100) :: string
LOGICAL :: foundValue
REAL (KIND = float) :: upperIn
REAL (KIND = float) :: lowerIn
REAL (KIND = double) :: upperOut
REAL (KIND = double) :: lowerOut
REAL (KIND = float) :: bias
!------------end of declaration------------------------------------------------
!inizialization
foundValue = .FALSE.
!find columns to be processed
DO i = 1, tab % noCols
string = StringCompact (StringToUpper (tab % col (i) % header) )
IF ( string == StringToUpper(keyIn) ) THEN
colIn => tab % col (i) !colIn is an alias of the input column
ELSE IF ( string == StringToUpper(keyOut) ) THEN
colOut => tab % col (i) !colOut is an alias of the output column
END IF
END DO
SELECT CASE ( StringToUpper (match) )
CASE ('EXACT')
!bound method is not necessary, only fixed makes sense.
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) == valueIn ) THEN
foundValue = .TRUE.
valueOut = StringToDouble (colout % row (i))
END IF
END DO
IF ( .NOT. foundValue ) THEN
CALL Catch ('error', 'TableLib', &
TRIM ( ToString (valueIn) ) // ' not found in table: ' , &
argument = tab % id )
END IF
CASE ('LINEAR')
!if bound is not specified, assume FIXED
IF (.NOT. PRESENT (bound) ) THEN
IF ( StringToFloat (colIn % row (1)) > valueIn .OR. &
StringToFloat (colIn % row (tab % noRows) ) < valueIn ) THEN
CALL Catch ('error', 'TableLib', 'bounds exceeded in table: ', &
argument = TRIM(tab % Id) )
END IF
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToDouble ( colOut % row (i) )
upperOut = StringToDouble ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
ELSE
SELECT CASE ( StringToUpper (bound) )
CASE ('FIXED')
IF ( StringToFloat (colIn % row (1)) > valueIn .OR. &
StringToFloat (colIn % row (tab % noRows) ) < valueIn ) THEN
CALL Catch ('error', 'TableLib', 'bounds exceeded in table: ', &
argument = TRIM(tab % Id) )
END IF
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToDouble ( colOut % row (i) )
upperOut = StringToDouble ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
CASE ('EXTENDLINEAR')
!If value exceed lower bound
IF ( StringToFloat (colIn % row (1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (1) )
upperIn = StringToFloat ( colIn % row (2) )
lowerOut = StringToDouble ( colOut % row (1) )
upperOut = StringToDouble ( colOut % row (2) )
!calculate interpolation
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
CALL Catch ('warning', 'TableLib', &
'value is under lower bound: extending linearly')
!if value exceed upper bound
ELSE IF ( StringToFloat (colIn % row (tab % noRows)) <= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row ( tab % noRows - 1) )
upperIn = StringToFloat ( colIn % row ( tab % noRows ) )
lowerOut = StringToDouble ( colOut % row ( tab % noRows - 1) )
upperOut = StringToDouble ( colOut % row ( tab % noRows ) )
!calculate interpolation
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
CALL Catch ('warning', 'TableLib', &
'value is over upper bound: extending linearly')
ELSE !value is between the boundary of the table
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToDouble ( colOut % row (i) )
upperOut = StringToDouble ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
END IF
CASE ('EXTENDCONSTANT')
!If value exceed lower bound
IF ( StringToFloat (colIn % row (1)) >= valueIn ) THEN
valueOut = StringToDouble (colOut % row (1))
CALL Catch ('warning', 'TableLib', &
'value is under lower bound: extending constantly')
!if value exceed upper bound
ELSE IF ( StringToFloat (colIn % row (tab % noRows)) <= valueIn ) THEN
valueOut = StringToFloat (colOut % row (tab % noRows))
CALL Catch ('warning', 'TableLib', &
'value is over upper bound: extending constantly')
ELSE !value is between the boundary of the table
!search for upper and lower value to interpolate between
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) <= valueIn .AND. &
StringToFloat (colIn % row (i+1)) >= valueIn ) THEN
lowerIn = StringToFloat ( colIn % row (i) )
upperIn = StringToFloat ( colIn % row (i+1) )
lowerOut = StringToDouble ( colOut % row (i) )
upperOut = StringToDouble ( colOut % row (i+1) )
EXIT
END IF
END DO
valueOut = LinearInterp ( lowerIn, upperIn, lowerOut, upperOut, valueIn )
END IF
CASE DEFAULT
CALL Catch ('error', 'TableLib', &
'unknown option in call to TableGetValue: ' , &
code = unknownOption, argument = TRIM(bound) )
END SELECT
END IF
CASE ('NEAREST')
!bound method is not necessary, only fixed makes sense.
bias = HUGE (bias) !initializa bias to biggest number
DO i = 1, tab % noRows
IF ( ABS ( StringToFloat (colIn % row (i)) - valueIn ) < bias ) THEN
bias = ABS ( StringToFloat (colIn % row (i)) - valueIn )
valueOut = StringToDouble (colOut % row (i))
END IF
END DO
CASE DEFAULT
CALL Catch ('error', 'TableLib', &
'unknown option in call to TableGetValue: ' , &
code = unknownOption, argument = TRIM(match) )
END SELECT
END SUBROUTINE TableGetDouble
!==============================================================================
!| Description:
! returns a double from column defined by keyOut corresponding to valueIn
! contained in column defined by keyIn. Table is identified by its id.
! Arguments:
! `valueIn` input value
! `tables` collection of tables to search in
! `id` id of the table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
! `match` method to match input value. Possible values are:
! 'exact' = column must contain exact input value
! 'linear' = calculates linear interpolation between two
! bounding values
! 'nearest' = search for the nearest value in input column
! `bound` method to manage bounds. Possible values are:
! 'fixed' = extreme values are treated as a wall
! 'extendlinear' = extend bounds with linear interpolation
! of last two extreme values
! 'extendconstant' = extend bounds preserving extreme value constant
SUBROUTINE TablesGetDouble &
!
( valueIn, tables, id, keyIn, keyOut, match, valueOut, bound )
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: id
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
CHARACTER (LEN = *), INTENT (IN) :: match
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: bound
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
! Scalar arguments with intent(in):
REAL (KIND = double), INTENT (OUT) :: valueOut
!------------end of declaration------------------------------------------------
IF ( PRESENT (bound) ) THEN
CALL TableGetDouble ( valueIn, tables % elem ( TableSyncById (tables, id) ), &
keyIn, keyOut, match, valueOut, bound )
ELSE
CALL TableGetDouble ( valueIn, tables % elem ( TableSyncById (tables, id) ), &
keyIn, keyOut, match, valueOut)
END IF
END SUBROUTINE TablesGetDouble
!==============================================================================
!| Description:
! returns a string from column defined by keyOut corresponding to valueIn
! contained in column defined by keyIn.
! Arguments:
! `valueIn` input value
! `tab` table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
SUBROUTINE TableGetString &
!
( valueIn, tab, keyIn, keyOut, valueOut )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringToFloat, ToString
USE LogLib, ONLY : &
! Imported Routines:
Catch
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
! Type defined arguments with intent (in):
TYPE (Table), INTENT (IN) :: tab
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (OUT) :: valueOut
! Local scalars:
TYPE (Column), POINTER :: colIn
TYPE (Column), POINTER :: colOut
INTEGER (KIND = short) :: i
CHARACTER (LEN = 100) :: string
LOGICAL :: foundValue
!------------end of declaration------------------------------------------------
!inizialization
foundValue = .FALSE.
!find columns to be processed
DO i = 1, tab % noCols
string = StringCompact (StringToUpper (tab % col (i) % header) )
IF ( string == StringToUpper(keyIn) ) THEN
colIn => tab % col (i) !colIn is an alias of the input column
ELSE IF ( string == StringToUpper(keyOut) ) THEN
colOut => tab % col (i) !colOut is an alias of the output column
END IF
END DO
DO i = 1, tab % noRows
IF ( StringToFloat (colIn % row (i)) == valueIn ) THEN
foundValue = .TRUE.
valueOut = colout % row (i)
END IF
END DO
IF ( .NOT. foundValue ) THEN
CALL Catch ('error', 'TableLib', &
TRIM ( ToString (valueIn) ) // ' not found in table: ' , &
argument = tab % id )
END IF
RETURN
END SUBROUTINE TableGetString
!==============================================================================
!| Description:
! returns a string from column defined by keyOut corresponding to valueIn
! contained in column defined by keyIn. Table is identified by its id.
! Arguments:
! `valueIn` input value
! `tables` collection of tables to search in
! `id` id of the table to search in
! `keyIn` defines header of the column of the input value
! `keyOut` defines header of the column of the output value
SUBROUTINE TablesGetString &
!
( valueIn, tables, id, keyIn, keyOut, valueOut)
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
REAL (KIND = float), INTENT (IN) :: valueIn
CHARACTER (LEN = *), INTENT (IN) :: id
CHARACTER (LEN = *), INTENT (IN) :: keyIn
CHARACTER (LEN = *), INTENT (IN) :: keyOut
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (OUT) :: valueOut
!------------end of declaration------------------------------------------------
CALL TableGetString ( valueIn, tables % elem ( TableSyncById (tables, id) ), &
keyIn, keyOut, valueOut)
END SUBROUTINE TablesGetString
!==============================================================================
!| Description:
! returns the position of table in collection of tables identified by id.
! Arguments:
! `tables` collection of tables to search in
! `id` id of the table
FUNCTION TableSyncById &
( tables, id ) &
RESULT (pos)
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringToUpper
USE LogLib, ONLY : &
! Imported Routines:
Catch
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: id
! Type defined arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
! Scalar arguments with intent(OUT):
INTEGER (KIND = long) :: pos
!Local variables:
LOGICAL :: foundTable
INTEGER (KIND = long) :: i
!------------end of declaration------------------------------------------------
foundTable = .FALSE.
DO i = 1, tables % number
IF ( StringToUpper (tables % elem (i) % id) == StringToUpper (id) ) THEN
foundTable = .TRUE.
pos = i
EXIT
END IF
END DO
IF ( .NOT. foundTable ) THEN
CALL Catch ('error', 'TableLib', &
'table not found in collection of tables: ' , &
argument = id )
END IF
END FUNCTION TableSyncById
!==============================================================================
!| Description:
! search the file for beginning of next table defined by keyword Table Start
! Arguments:
! `unit` file in which operate search
! `id` optional, table id
! `line` optional, line of file to begin search
! Result:
! Return -1 when table is not found
! line of beginning of a table
FUNCTION TableFileSync &
( unit, id, line ) &
RESULT (code)
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringSplit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: unit
CHARACTER (LEN = *), OPTIONAL, INTENT(IN) :: id
! Scalar arguments with intent (inout):
INTEGER (KIND = long), OPTIONAL, INTENT (INOUT) :: line
! Local scalars:
INTEGER (KIND = short) :: code
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i
CHARACTER (LEN = 300) :: string
INTEGER (KIND = long) :: iLine
INTEGER (KIND = long) :: iLineTablestart
CHARACTER (LEN = 300) :: idLocal
CHARACTER (LEN = 300) :: before
!------------end of declaration------------------------------------------------
code = -1
iLine = 0
!REWIND (unit)
IF ( PRESENT (line) ) THEN !Sync file to specified line
REWIND (unit)
DO i =1, line
READ(unit,*)
END DO
iLine = line
END IF
ios = 0
DO WHILE (ios >= 0)
READ (unit, "(a)",IOSTAT = ios) string
iLine = iLine + 1
IF (PRESENT (line) ) THEN
line = line + 1
END IF
IF ( StringCompact (StringToUpper (string) ) == "TABLE START" ) THEN
iLineTablestart = iLine
IF (PRESENT(id)) THEN
DO WHILE (StringCompact (StringToUpper (string) ) /= "TABLE END" )
READ (unit, "(a)",IOSTAT = ios) string
iLine = iLine + 1
IF ( StringCompact (StringToUpper (string(1:3)) ) == "ID:" ) THEN
string = StringCompact (StringToUpper (string(4:LEN_TRIM(string))))
CALL StringSplit ( '#', string, before) !remove inline comment
idLocal = before
IF (idLocal == StringToUpper (id)) THEN
REWIND (unit)
DO i =1, iLineTablestart
READ(unit,*)
END DO
code = 1
RETURN
END IF
EXIT
END IF
END DO
ELSE
code = 1
RETURN
END IF
END IF
END DO
END FUNCTION TableFileSync
!==============================================================================
!| Description:
! Read the title of the table. Title is optional.
! Arguments:
! `lines` collections of lines
! Result:
! Return title if exists
FUNCTION TableReadTitle &
( lines ) &
RESULT (title)
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringSplit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Local scalars:
CHARACTER (LEN = 300) :: title
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = 300) :: before
LOGICAL :: titleFound
!------------end of declaration------------------------------------------------
string = ''
titleFound = .FALSE.
! scan table
DO i = 1, SIZE (lines)
string = lines (i)
CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( before(1:5)) == "TITLE" ) THEN !found title
CALL StringSplit ( '#', string, before) !remove inline comment
title = before
titleFound = .TRUE.
RETURN
END IF
END DO
IF ( .NOT. titleFound ) THEN
title = ''
!Title is not mandatory element of a table.
END IF
END FUNCTION TableReadTitle
!==============================================================================
!| Description:
! Read the Id of the table. Id is mandatory and must be unique.
! Arguments:
! `lines` collections of lines
! Result:
! Return Id
FUNCTION TableReadId &
( lines ) &
RESULT (id)
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringSplit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Local scalars:
CHARACTER (LEN = 300) :: id
INTEGER (KIND = short) :: ios
INTEGER (KIND = short) :: i
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = 300) :: before
LOGICAL :: idFound
!------------end of declaration------------------------------------------------
string = ''
idFound = .FALSE.
! scan table
DO i = 1, SIZE (lines)
string = lines (i)
CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( before(1:2)) == "ID" ) THEN !found id
CALL StringSplit ( '#', string, before) !remove inline comments
id = before
idFound = .TRUE.
RETURN
END IF
END DO
IF ( .NOT. idFound ) THEN !Id is mandatory in a table
CALL Catch ('error', 'TableLib', 'Table Id not found')
END IF
END FUNCTION TableReadId
!==============================================================================
!| Description:
! Return a list of Ids from TableCollection
! Arguments:
! `tables` collections of tbles
! Result:
! Return Ids
FUNCTION TablesGetIds &
( tables ) &
RESULT (ids)
IMPLICIT NONE
! Arguments with intent(in):
TYPE (TableCollection), INTENT (IN) :: tables
! Local declarations:
CHARACTER (LEN = 30), ALLOCATABLE :: ids (:)
INTEGER (KIND = short) :: i
!------------end of declaration------------------------------------------------
!allocate array
IF ( ALLOCATED (ids) ) THEN
DEALLOCATE (ids)
END IF
ALLOCATE ( ids (tables % number) )
DO i = 1, tables % number
ids (i) = tables % elem (i) % id
END DO
RETURN
END FUNCTION TablesGetIds
!==============================================================================
!| Description:
! read a collection of tables from specified file.
! Arguments:
! `file` file in which table is contained
! `tables` returned collection of tables
SUBROUTINE TablesGetFromFile &
( file, tables )
USE Utilities, ONLY: &
!Imported routines:
GetUnit
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: file
! Array arguments with intent (out):
TYPE (TableCollection), INTENT (OUT) :: tables
! Local scalars:
INTEGER (KIND = short) :: iunit
INTEGER (KIND = short) :: ios
INTEGER (KIND = long) :: count
INTEGER (KIND = long) :: i, j
CHARACTER (LEN = 300) :: string
! Local Arrays:
CHARACTER (LEN = LINELENGTH), POINTER :: lines (:)
!------------end of declaration------------------------------------------------
!get a free fortran unit
iunit = GetUnit ()
OPEN (UNIT = iunit, FILE = file, STATUS = "old")
!count the number of tables present in the file
ios = 0
count = 0
DO WHILE (ios >= 0)
READ (iunit, "(a)",IOSTAT = ios) string
IF ( StringCompact (StringToUpper (string) ) == "TABLE START" ) THEN
count = count + 1
END IF
END DO
!allocate space for tables
ALLOCATE ( tables % elem (count) )
tables % number = count
!initialize tables from file
REWIND (iunit)
i = 0
DO i = 1, count
!search beginning of next table
ios = TableFileSync (iunit)
!Store significant lines in memory
CALL TableStoreLines ( iunit, lines )
!Get title
tables % elem (i) % title = TableReadTitle (lines)
!get Id
tables % elem (i) % id = TableReadId (lines)
!check that id is not replicated
CALL CheckId(tables,i)
!count number of columns
tables % elem (i) % noCols = TableCountCols (lines)
IF ( tables % elem (i) % noCols == 0) THEN
CALL Catch ('error', 'TableLib', 'no columns found in table: ', &
argument = tables % elem (i) % id)
END IF
!allocate columns
ALLOCATE ( tables % elem (i) % col ( tables % elem (i) % noCols ) )
!count number of rows
tables % elem (i) % noRows = TableCountRows (lines)
!allocate rows
DO j = 1, tables % elem (i) % noCols
ALLOCATE ( tables % elem (i) % col (j) % row ( tables % elem (i) % noRows ) )
END DO
!read header unit and content of the tables.
CALL TableReadHeader ( lines, tables % elem (i) )
CALL TableReadUnit ( lines, tables % elem (i) )
CALL TableReadContent ( lines, tables % elem (i) )
! table is in memory: deallocate lines
DEALLOCATE (lines)
END DO
!tables are initialized: close file
CLOSE (iunit)
END SUBROUTINE TablesGetFromFile
!==============================================================================
!| Description:
! read a collection of tables from specified unit. File is already open.
! Arguments:
! `unit` unit of file in which table is contained
! `tables` returned collection of tables
SUBROUTINE TablesGetFromUnit &
( unit, tables )
USE Utilities, ONLY: &
!Imported routines:
GetUnit
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: unit
! Array arguments with intent (out):
TYPE (TableCollection), INTENT (OUT) :: tables
! Local scalars:
INTEGER (KIND = short) :: ios
INTEGER (KIND = long) :: count
INTEGER (KIND = long) :: i, j
CHARACTER (LEN = 300) :: string
! Local Arrays:
CHARACTER (LEN = LINELENGTH), POINTER :: lines (:)
!------------end of declaration------------------------------------------------
!count the number of tables present in the file
ios = 0
count = 0
DO WHILE (ios >= 0)
READ (unit, "(a)",IOSTAT = ios) string
IF ( StringCompact (StringToUpper (string) ) == "TABLE START" ) THEN
count = count + 1
END IF
END DO
!allocate space for tables
ALLOCATE ( tables % elem (count) )
tables % number = count
!initialize tables from file
REWIND (unit)
i = 0
DO i = 1, count
!search beginning of next table
ios = TableFileSync (unit)
!Store significant lines in memory
CALL TableStoreLines ( unit, lines )
!Get title
tables % elem (i) % title = TableReadTitle (lines)
!get Id
tables % elem (i) % id = TableReadId (lines)
!check that id is not replicated
CALL CheckId(tables,i)
!count number of columns
tables % elem (i) % noCols = TableCountCols (lines)
IF ( tables % elem (i) % noCols == 0) THEN
CALL Catch ('error', 'TableLib', 'no columns found in table: ', &
argument = tables % elem (i) % id)
END IF
!allocate columns
ALLOCATE ( tables % elem (i) % col ( tables % elem (i) % noCols ) )
!count number of rows
tables % elem (i) % noRows = TableCountRows (lines)
!allocate rows
DO j = 1, tables % elem (i) % noCols
ALLOCATE ( tables % elem (i) % col (j) % row ( tables % elem (i) % noRows ) )
END DO
!read header unit and content of the tables.
CALL TableReadHeader ( lines, tables % elem (i) )
CALL TableReadUnit ( lines, tables % elem (i) )
CALL TableReadContent ( lines, tables % elem (i) )
! table is in memory: deallocate lines
DEALLOCATE (lines)
END DO
END SUBROUTINE TablesGetFromUnit
!==============================================================================
!| Description:
! read a table from specified file. File is not yet open.
! If id is not specified, in a file containing multiple tables,
! the first table is read
! Arguments:
! `file` file in which table is contained
! `tab` returned table
! `id` optional, id of table to read
SUBROUTINE TableGetFromFile &
( file, tab, id )
USE Utilities, ONLY: &
!Imported routines:
GetUnit
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = *), INTENT (IN) :: file
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: id
! Array arguments with intent (out):
TYPE (Table), INTENT (OUT) :: tab
! Local scalars:
INTEGER (KIND = short) :: iunit
INTEGER (KIND = short) :: ios
INTEGER (KIND = long) :: count
INTEGER (KIND = long) :: j
CHARACTER (LEN = 300) :: string
! Local Arrays:
CHARACTER (LEN = LINELENGTH), POINTER :: lines (:)
!------------end of declaration------------------------------------------------
!get a free fortran unit
iunit = GetUnit ()
OPEN (UNIT = iunit, FILE = file, STATUS = "old")
!search beginning of table
IF (PRESENT(id)) THEN
ios = TableFileSync (iunit, id = id)
ELSE
ios = TableFileSync (iunit)
END IF
!check if table was found
IF (ios == -1) THEN
CALL Catch ('error', 'TableLib', 'Table not found in file: ', &
argument = file)
RETURN
CLOSE (iunit)
END IF
!Store significant lines in memory
CALL TableStoreLines ( iunit, lines )
!Get title
tab % title = TableReadTitle (lines)
!get Id
tab % id = TableReadId (lines)
!count number of columns
tab % noCols = TableCountCols (lines)
IF ( tab % noCols == 0) THEN
CALL Catch ('error', 'TableLib', 'no columns found in table: ', &
argument = tab % id)
END IF
!allocate columns
ALLOCATE ( tab % col ( tab % noCols ) )
!count number of rows
tab % noRows = TableCountRows (lines)
!allocate rows
DO j = 1, tab % noCols
ALLOCATE ( tab % col (j) % row ( tab % noRows ) )
END DO
!read header unit and content of the tables.
CALL TableReadHeader ( lines, tab )
CALL TableReadUnit ( lines, tab )
CALL TableReadContent ( lines, tab )
!table is initialized: close file and deallocate lines
CLOSE (iunit)
DEALLOCATE (lines)
END SUBROUTINE TableGetFromFile
!==============================================================================
!| Description:
! read a table from specified file unit. File is already open.
! Arguments:
! `unit` file in which table is contained
! `tab` returned table
! `id` optional, id of table to read
SUBROUTINE TableGetFromUnit &
( unit, tab, id )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: unit
CHARACTER (LEN = *), OPTIONAL, INTENT (IN) :: id
! Array arguments with intent (out):
TYPE (Table), INTENT (OUT) :: tab
! Local scalars:
INTEGER (KIND = short) :: ios
INTEGER (KIND = long) :: count
INTEGER (KIND = long) :: j
CHARACTER (LEN = 300) :: string
! Local Arrays:
CHARACTER (LEN = LINELENGTH), POINTER :: lines (:)
!------------end of declaration------------------------------------------------
!search beginning of table
IF (PRESENT(id)) THEN
ios = TableFileSync (unit, id = id)
ELSE
ios = TableFileSync (unit)
END IF
!Store significant lines in memory
CALL TableStoreLines ( unit, lines )
!Get title
tab % title = TableReadTitle (lines)
!get Id
tab % id = TableReadId (lines)
!count number of columns
tab % noCols = TableCountCols (lines)
IF ( tab % noCols == 0) THEN
CALL Catch ('error', 'TableLib', 'no columns found in table: ', &
argument = tab % id)
END IF
!allocate columns
ALLOCATE ( tab % col ( tab % noCols ) )
!count number of rows
tab % noRows = TableCountRows (lines)
!allocate rows
DO j = 1, tab % noCols
ALLOCATE ( tab % col (j) % row ( tab % noRows ) )
END DO
!read header unit and content of the tables.
CALL TableReadHeader ( lines, tab )
CALL TableReadUnit ( lines, tab )
CALL TableReadContent ( lines, tab )
!table is initialized: deallocate lines
DEALLOCATE (lines)
END SUBROUTINE TableGetFromUnit
!==============================================================================
!| Description:
! create a new table from scratch and initialize variables
! Arguments:
! `tab` returned table
SUBROUTINE TableCreate &
( tab )
IMPLICIT NONE
! Arguments with intent (out):
TYPE (Table), INTENT (OUT) :: tab
!------------end of declaration------------------------------------------------
tab % title = ''
tab % id = ''
tab % noRows = 0
tab % noCols = 0
NULLIFY ( tab % col)
RETURN
END SUBROUTINE TableCreate
!==============================================================================
!| Description:
! set table id
! Arguments:
! `tab` returned table
! `id` table id
SUBROUTINE TableSetId &
( tab, id )
IMPLICIT NONE
! Arguments with intent(in):
CHARACTER ( LEN = *), INTENT(IN) :: id
! Arguments with intent (inout):
TYPE (Table), INTENT (INOUT) :: tab
!------------end of declaration------------------------------------------------
tab % id = TRIM (id)
RETURN
END SUBROUTINE TableSetId
!==============================================================================
!| Description:
! set table title
! Arguments:
! `tab` returned table
! `title` table title
SUBROUTINE TableSetTitle &
( tab, title )
IMPLICIT NONE
! Arguments with intent(in):
CHARACTER ( LEN = *), INTENT(IN) :: title
! Arguments with intent (inout):
TYPE (Table), INTENT (INOUT) :: tab
!------------end of declaration------------------------------------------------
tab % title = TRIM (title)
RETURN
END SUBROUTINE TableSetTitle
!==============================================================================
!| Description:
! set number of rows and columns and allocate variables
! Arguments:
! `tab` returned table
! `nrow` number of rows
! `ncol` number of columns
SUBROUTINE TableSetRowCol &
( tab, nrow, ncol )
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT(IN) :: nrow
INTEGER (KIND = short), INTENT(IN) :: ncol
! Arguments with intent (inout):
TYPE (Table), INTENT (INOUT) :: tab
! local declarations:
INTEGER (KIND = short) :: i
!------------end of declaration------------------------------------------------
tab % noRows = nrow
tab % noCols = ncol
IF ( ASSOCIATED ( tab % col ) ) THEN
DEALLOCATE ( tab % col )
END IF
ALLOCATE ( tab % col (ncol) )
DO i = 1, ncol
tab % col (i) % header = ''
tab % col (i) % unit = ''
ALLOCATE ( tab % col (i) % row (nrow) )
tab % col (i) % row = ''
END DO
RETURN
END SUBROUTINE TableSetRowCol
!==============================================================================
!| Description:
! set header of a specified column
! Arguments:
! `tab` returned table
! `col` column to be changed
! `header` header string
SUBROUTINE TableSetColHeader &
( tab, col, header)
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: col
CHARACTER (LEN = *), INTENT (IN) :: header
! Arguments with intent (inout):
TYPE (Table), INTENT (INOUT) :: tab
!------------end of declaration------------------------------------------------
tab % col (col) % header = TRIM (header)
RETURN
END SUBROUTINE TableSetColHeader
!==============================================================================
!| Description:
! set unit of a specified column
! Arguments:
! `tab` returned table
! `col` column to be changed
! `unit` unit string
SUBROUTINE TableSetColUnit &
( tab, col, unit)
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: col
CHARACTER (LEN = *), INTENT (IN) :: unit
! Arguments with intent (inout):
TYPE (Table), INTENT (INOUT) :: tab
!------------end of declaration------------------------------------------------
tab % col (col) % unit = TRIM (unit)
RETURN
END SUBROUTINE TableSetColUnit
!==============================================================================
!| Description:
! set row content
! Arguments:
! `tab` returned table
! `row` column to be changed
! `content` string array to fill in row
SUBROUTINE TableFillRow &
( tab, row, content)
IMPLICIT NONE
! Arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: row
CHARACTER (LEN = *), INTENT (IN) :: content (:)
! Arguments with intent (inout):
TYPE (Table), INTENT (INOUT) :: tab
!local declarations:
INTEGER (KIND = short) :: i
!------------end of declaration------------------------------------------------
DO i = 1, tab % noCols
tab % col (i) % row (row) = TRIM ( content (i) )
END DO
RETURN
END SUBROUTINE TableFillRow
!==============================================================================
!| Description:
! read the content of the table.
! Arguments:
! `lines` collection of strings that contain table information
! `tab` table to update
SUBROUTINE TableReadContent &
( lines, tab )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringSplit, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Array arguments with intent (out):
TYPE (Table), INTENT (OUT) :: tab
! Local scalars:
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = LINELENGTH) :: before
INTEGER (KIND = long) :: i, j, k
! Local Arrays:
!------------end of declaration------------------------------------------------
string = ''
i = 0
! scan table to find lines that have not a keyword.
DO k = 1, SIZE (lines)
string = lines (k)
!CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( string(1:6)) == "TITLE:" .OR. &
StringToUpper ( string(1:3)) == "ID:" .OR. &
StringToUpper ( string(1:6)) == "UNITS:" .OR. &
StringToUpper ( string(1:8)) == "COLUMNS:" .OR. &
StringToUpper ( string(1:11)) == "TABLE START" .OR. &
StringToUpper ( string(1:9)) == "TABLE END") THEN
! this is a line with a keyword not a row of table
ELSE
!remove inline comments
CALL StringSplit ( '#', string, before) !remove inline comments
string = before
!increment row
i = i + 1
READ(string,*) ( tab % col (j) % row (i), j = 1, tab % noCols )
END IF
END DO
END SUBROUTINE TableReadContent
!==============================================================================
!| Description:
! read unit of the columns of the table.
! Arguments:
! `lines` collection of strings that contain table information
! `tab` table to update
SUBROUTINE TableReadUnit &
( lines, tab )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringSplit, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Array arguments with intent (out):
TYPE (Table), INTENT (OUT) :: tab
! Local scalars:
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = LINELENGTH) :: before
INTEGER (KIND = long) :: i
INTEGER (KIND = long) :: par1, par2
! Local Arrays:
!------------end of declaration------------------------------------------------
string = ''
! scan table to find line denoted by units keyword.
DO i = 1, SIZE (lines)
string = lines (i)
CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( before(1:5)) == "UNITS" ) THEN !found units
CALL StringSplit ( '#', string, before) !remove inline comments
string = before
EXIT
END IF
END DO
!search for headers
DO i = 1, tab % noCols
par1 = INDEX ( string, '[' )
par2 = INDEX ( string, ']' )
tab % col (i) % unit = string ( par1+1 : par2-1 )
!erase part of the string already processed
string = string ( par2+1 : LEN_TRIM (string) )
END DO
END SUBROUTINE TableReadUnit
!==============================================================================
!| Description:
! read header of the columns of the table.
!Arguments:
! `lines` collection of strings that contain table information
! `tab` table to update
SUBROUTINE TableReadHeader &
( lines, tab )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringSplit, StringToUpper
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Array arguments with intent (out):
TYPE (Table), INTENT (OUT) :: tab
! Local scalars:
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = LINELENGTH) :: before
INTEGER (KIND = long) :: i
INTEGER (KIND = long) :: par1, par2
! Local Arrays:
!------------end of declaration------------------------------------------------
string = ''
! scan table to find line denoted to columns keyword.
DO i = 1, SIZE (lines)
string = lines (i)
CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( before(1:7)) == "COLUMNS" ) THEN !found columns
CALL StringSplit ( '#', string, before) !remove inline comments
string = before
EXIT
END IF
END DO
!search for headers
DO i = 1, tab % noCols
par1 = INDEX ( string, '[' )
par2 = INDEX ( string, ']' )
tab % col (i) % header = string ( par1+1 : par2-1 )
!erase part of the string already processed
string = string ( par2+1 : LEN_TRIM (string) )
END DO
END SUBROUTINE TableReadHeader
!==============================================================================
!| Description:
! Count the number of columns in a table stored in a collection of lines.
!Method:
! count the number of tokens included in parentheses [].
! Arguments:
! `lines` collections of lines
! Result:
! Return number of columns
FUNCTION TableCountCols &
( lines ) &
RESULT (cols)
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, StringSplit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Local scalars:
INTEGER (KIND = short) :: cols
INTEGER (KIND = short) :: i
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = 300) :: before
CHARACTER (LEN = 1) :: ch
LOGICAL :: columnsFound
LOGICAL :: parOpen
!------------end of declaration------------------------------------------------
string = ''
columnsFound = .FALSE.
cols = 0
! scan table to find line denoted to columns keyword.
DO i = 1, SIZE (lines)
string = lines (i)
CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( before(1:7)) == "COLUMNS" ) THEN !found columns
CALL StringSplit ( '#', string, before) !remove inline comments
string = before
columnsFound = .TRUE.
EXIT
END IF
END DO
IF ( .NOT. columnsFound ) THEN
CALL Catch ('error', 'TableLib', 'Keyword columns not found')
END IF
!count the number of couples of parentheses (), [], {} or <>
parOpen = .FALSE.
DO i = 1, LEN_TRIM (string)
ch = string (i:i)
IF ( ch == "[" ) THEN
IF (parOpen ) THEN
CALL Catch ('error', 'TableLib', 'parentheses in columns was not closed')
END IF
parOpen = .TRUE.
cols = cols + 1
ELSE IF ( ch == "]") THEN
IF (.NOT. parOpen ) THEN
CALL Catch ('error', 'TableLib', 'parentheses in columns was not opened')
END IF
parOpen = .FALSE.
END IF
END DO
!if parentheses are not closed, log an error
IF (parOpen ) THEN
CALL Catch ('error', 'TableLib', 'parentheses in columns was not closed')
END IF
END FUNCTION TableCountCols
!==============================================================================
!| Description:
! Count the number of rowss in a table stored in a collection of lines.
! Method:
! count the number of non blank lines that have not a keyword.
! Arguments:
! `lines` collections of lines
! Result:
! Return number of rows
FUNCTION TableCountRows &
( lines ) &
RESULT (rows)
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringToUpper, StringSplit
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
CHARACTER (LEN = LINELENGTH), INTENT (IN), POINTER :: lines (:)
! Local scalars:
INTEGER (KIND = short) :: rows
INTEGER (KIND = short) :: i
CHARACTER (LEN = LINELENGTH) :: string
CHARACTER (LEN = 300) :: before
!------------end of declaration------------------------------------------------
string = ''
rows = 0
! scan table to count lines that have not a keyword.
DO i = 1, SIZE (lines)
string = lines (i)
CALL StringSplit ( ':', string, before)
IF ( StringToUpper ( before(1:5)) == "TITLE" .OR. &
StringToUpper ( before(1:2)) == "ID" .OR. &
StringToUpper ( before(1:5)) == "UNITS" .OR. &
StringToUpper ( before(1:7)) == "COLUMNS" .OR. &
StringToUpper ( before(1:11)) == "TABLE START" .OR. &
StringToUpper ( before(1:9)) == "TABLE END") THEN
! this is a line with a keyword not a row of table
ELSE
rows = rows + 1
END IF
END DO
END FUNCTION TableCountRows
!==============================================================================
! Description:
! Check that table Id is not duplicated. Table id must be unique.
! Arguments:
! `tables` collections of tables
! `pos` position of the last Id entry
SUBROUTINE CheckId &
( tables, pos )
IMPLICIT NONE
! Function arguments
! Scalar arguments with intent(in):
INTEGER (KIND = long), INTENT(IN) :: pos
! Array derived type with intent(in):
TYPE (TableCollection), INTENT(IN) :: tables
! Local scalars:
INTEGER (KIND = long) :: i
!------------end of declaration------------------------------------------------
!scan all Ids
DO i = 1, pos - 1
IF ( tables % elem(i) % id == tables % elem(pos) % id ) THEN
CALL Catch ('error', 'TableLib', 'Duplicate table id: ', &
argument = tables % elem(pos) % id )
END IF
END DO
END SUBROUTINE CheckId
!==============================================================================
!| Description:
! read the lines of a table which are stored in an array of strings.
! Non significative lines (i.e. comments or blank lines) are ignored.
! Subroutine supposes that the cursor is sync to the first line after
! the keyword 'Table Start'. hence it is must benn called after
! a call to tableFileSync.
!Arguments:
! `unit` file in which table is contained
! `lines` returned collection of linestable
SUBROUTINE TableStoreLines &
( unit, lines )
! Module used:
USE StringManipulation, ONLY: &
! imported routines:
StringCompact, StringToUpper, &
StringSplit
IMPLICIT NONE
! Subroutine arguments
! Scalar arguments with intent(in):
INTEGER (KIND = short), INTENT (IN) :: unit
! Array arguments with intent (out):
CHARACTER (LEN = LINELENGTH), INTENT (OUT), POINTER :: lines (:)
! Local scalars:
INTEGER (KIND = short) :: ios
CHARACTER (LEN = LINELENGTH) :: string
INTEGER (KIND = long) :: count
INTEGER (KIND = long) :: i
! Local Type definition:
!define a dynamic list of strings
TYPE LinkedList
TYPE(LinkedList), POINTER :: next
CHARACTER (LEN = LINELENGTH) :: line
END TYPE LinkedList
! Local Arrays:
TYPE (LinkedList), POINTER :: list
TYPE (LinkedList), POINTER :: current
TYPE (LinkedList), POINTER :: next
TYPE (LinkedList), POINTER :: previous
!------------end of declaration------------------------------------------------
!initialization
string = ''
count = 0
NULLIFY (list)
! scan file till end of the table keyword TABLE END
DO WHILE ( .NOT. StringCompact (StringToUpper (string) ) == "TABLE END" )
READ (unit, "(a)",IOSTAT = ios) string
IF ( ios > 0 ) THEN !reached the end of file without finding table end
!CALL Catch
END IF
string = StringCompact (string)
IF ( string == '' .OR. string(1:1) == "#" ) THEN !skip element
ELSE !found new element
!increment counter
count = count + 1
!add an element to list
IF(.NOT.ASSOCIATED(list)) THEN
ALLOCATE(list) !riconosco il primo elemento da inserire
current => list
ELSE
ALLOCATE(current%next)
current => current%next
END IF
!store line in the list.
current % line = string
END IF
END DO
!allocate space for significant lines
ALLOCATE ( lines (count) )
!transfer lines from temporary list to tab
current => list ! current is an alias of list
DO i = 1, count
lines (i) = current % line
previous => current
current => current % next !current is an alias of next element of the list
DEALLOCATE(previous) !free memory of the previous element
END DO
END SUBROUTINE TableStoreLines
!==============================================================================
!| Description:
! return the number of rows in a table
FUNCTION TableGetNumberRows &
!
( tab ) &
!
RESULT (rows)
IMPLICIT NONE
!arguments with intent (in):
TYPE (table), INTENT (IN) :: tab
!local dclarations:
INTEGER :: rows
!--------------------------------end of declarations---------------------------
rows = tab % noRows
END FUNCTION TableGetNumberRows
!==============================================================================
!| Description:
! return the number of rows of a table in a table collection
FUNCTION TablesGetNumberRows &
!
( tables, id ) &
!
RESULT (rows)
IMPLICIT NONE
!arguments with intent (in):
TYPE (TableCollection), INTENT (IN) :: tables
CHARACTER (LEN = *), INTENT (IN) :: id
!local dclarations:
INTEGER :: rows
!--------------------------------end of declarations---------------------------
rows = tables % elem ( TableSyncById (tables, id) ) % noRows
END FUNCTION TablesGetNumberRows
END MODULE TableLib