String Handling
Fortran is not a language famous for its string handling capabilities, but due
to the presence of the input deck EPOCH has fairly extensive string handling
routines. Strings used are all of the standard Fortran CHARACTER
type and are defined as:
CHARACTER(LEN=string_length) :: string
string_length
is a global constant defined in
src/constants.F90
which can be increased to allow EPOCH to
handle longer strings. There may be reasons to increase this length if you wish
to use long complex expressions in the input deck. Note that many Fortran
compilers do not allow strings to exceed 512 characters in length.
Listed here are the string handling routines (other than those in the core
maths parser routine which are documented elsewhere) which are currently used
in EPOCH. These functions can be found in src/deck/strings.f90
and
src/deck/strings_advanced.f90
str_cmp
FUNCTION str_cmp(str_in, str_test)
CHARACTER(LEN=*), INTENT(IN) :: str_in, str_test
LOGICAL :: str_cmp
str_cmp
is the routine which does all the string comparisons in
EPOCH. It deals with leading and trailing whitespace automatically and
tests for length differences. It does not test for strings being
valid substrings of each other, only for full equality.
A developer should always use str_cmp
rather than doing their own
string testing to ensure consistent behaviour across the entire EPOCH code
base.
as_real_simple
FUNCTION as_real_simple(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
REAL(num) :: as_real_simple
as_real_simple
is a routine to convert a string into a real
number without invoking the maths parser. It can cope with standard form as
well as simple decimal reals. It is significantly faster than the maths parser,
but should only be used when the user explicitly shouldn’t be able to use
a mathematical expression. If the string cannot be parsed then the routine sets
the bitmask c_err_bad_value
on the parameter err
If you have a string which has to be converted into a real quickly then this is the routine to use. You probably shouldn’t use it when parsing a string from the input deck, since there is no reason to restrict the user from specifying a mathematical expression. The routine is used inside the maths parser to parse simple numbers.
as_integer_simple
FUNCTION as_integer_simple(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
INTEGER :: as_integer_simple
as_integer_simple
is a routine to convert a string into an
integer without invoking the maths parser. It can cope with standard form as
well as simple decimal integers. It is significantly faster than the maths
parser, but should only be used when the user explicitly shouldn’t be
able to use a mathematical expression. If the string cannot be parsed then the
routine sets the bitmask c_err_bad_value
on the parameter
err
This routine is used internally in several parts of the code when parsing
things like numbers which are parts of strings (i.e. the 1 in
direction1
for distribution functions). It probably shouldn’t
be used to directly parse input deck parameters, since there is no reason to
restrict the user from specifying mathematical expressions.
as_long_integer_simple
FUNCTION as_long_integer_simple(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
INTEGER(KIND=8) :: as_long_integer_simple
as_long_integer_simple
is equivalent to
as_integer_simple
, but returns the larger
INTEGER(KIND=8)
rather than a normal INTEGER(KIND=4)
.
as_boundary
FUNCTION as_boundary(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
INTEGER :: as_boundary
as_direction
is used when assigning a laser to a boundary and
recognises the strings
- x_min or left - c_bd_x_min.
- x_max or right - c_bd_x_max.
- y_min or down - c_bd_y_min.
- y_max or up - c_bd_y_max.
- z_min or back - c_bd_z_min.
- z_max or front - c_bd_z_max.
It returns the associated direction code (given after the dash in the definition).
If you’re writing code which requires attaching something to a boundary, whether a boundary condition, a diagnostic or some other routine, then this is the routine that should be used. Note that in order to prevent confusion when moving input decks between different dimension versions of EPOCH, each code only recognises the strings for boundaries that it actually has.
as_logical
FUNCTION as_logical(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
Logical :: as_logical
as_logical
simply tests for the strings “T” and “F” to
determine a boolean value. The default behaviour of as_logical
is
to treat any string that isn’t “T” as a false value.
You should use this rather than using a 0/1 boolean flag in the input deck for consistency.
split_off_int
SUBROUTINE split_off_int(str_in, str_out, int_out, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
CHARACTER(LEN=*), INTENT(OUT) :: str_out
INTEGER, INTENT(OUT) :: int_out
INTEGER, INTENT(INOUT) :: err
split_off_int
is a routine which splits a string of the format
string n
into a string string
and an integer n
which are
returned separately in the str_out
and int_out
parameters respectively. If it can’t split the string successfully then it
sets the c_err_bad_value
bitfield of the err parameter.
This is used in the core of the deck parser to deal with blocks like the numbered species blocks in the initial conditions, and also in some of the specific block parsers. Again, this routine should be used to split strings like this rather than coding a new routine.
split_range
SUBROUTINE split_range(str_in, real1, real2, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
REAL(num), INTENT(OUT) :: real1, real2
INTEGER, INTENT(INOUT) :: err
split_range
is a routine which splits a string of the format
(n, m)
into two reals n
and m
which are returned
separately in the real1
and real2
parameters
respectively. If it can’t split the string successfully then it sets the
c_err_bad_value
bitfield of the err parameter.
This is used when specifying ranges in the input decks at present. Any ranges which should be specified in a single parameter should be specified in this form and this routine used to split the string.
as_integer
FUNCTION as_integer(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
INTEGER :: as_integer
as_integer
is the routine which returns integers from strings
using the maths parser. If a mathematical expression resolves to a non-integer
result then this routine rounds to the NEAREST integer. There are explicit
rounding routines in the maths parser to force other behaviour.
This routine should be used when evaluating most strings into integers. Note
that this routine evaluates spatially dependent quantities at (0,0) on each
processor, so will give unpredictable results when spatially dependent
quantities are given to it (like density, bx etc.). To evaluate a spatially
varying quantity use evaluate_string_in_space
.
as_long_integer
FUNCTION as_long_integer(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
INTEGER(KIND=8) :: as_long_integer
as_long_integer
is the routine which returns long integers from
strings using the maths parser. If a mathematical expression resolves to a
non-integer result then this routine rounds to the NEAREST integer. There are
explicit rounding routines in the maths parser to force other behaviour.
This routine should be used when evaluating strings which are likely to be too large to be stored in an INTEGER(KIND=4).
as_real
FUNCTION as_real(str_in, err)
CHARACTER(LEN=*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
REAL(num) :: as_real
as_real
is the routine which returns reals from strings using the
maths parser.
This routine should be used when evaluating most strings into reals. Note that
this routine evaluates spatially dependent quantities at (0,0) on each
processor, so will give unpredictable results when spatially dependent
quantities are given to it (like density, bx etc.). To evaluate a spatially
varying quantity use evaluate_string_in_space
.
evaluate_string_in_space
SUBROUTINE evaluate_string_in_space(str_in, data_out, &
x1, x2, \{y1, y2, z1, z2,\} err)
CHARACTER(*), INTENT(IN) :: str_in
INTEGER, INTENT(INOUT) :: err
INTEGER, INTENT(IN) :: x1, x2\{, y1, y2, z1, z2\}
REAL(num), DIMENSION(1:,1:,1:), INTENT(OUT) :: data_out
evaluate_string_in_space
is a routine which is used to evaluate
a tokenized maths expression over a region of the domain. The dimensionality of
data_out
, and the presence or absence of y1, y2
and
z1, z2
depend on the dimensionality of the code being used. The
x1, x2, ...
parameters represent the indices in that
direction over which the expression should be evaluated. For example, in 2D to
evaluate an expression over the entire domain, the code would look like:
REAL(num), DIMENSION(:,:), ALLOCATABLE :: data
ALLOCATE(data(-2:nx+3,-2:ny+3))
CALL evaluate_string_in_space(string, data, -2, nx+3, -2, ny+3, err)
This routine is suitable to evaluate expressions over a subsection or all of the domain, and is used in this way in the initial condition deck parser routines. However, the routine does have one significant weakness, which is that it tokenizes the string each time it is called. Tokenizing the string is a time consuming process, so if the string is to be evaluated several times for different reasons (for example, the time profile for the laser) then a different procedure should be followed using the lower level parser routines.