NuMFor 9f2ab49 (2024-04-08)
Numerical (Modern) Fortran. Library for Simple Numerical computing
Submodule Utils

The module utils defines several objects (types, constants and routines) convenient for common tasks in programming.

It consists of several areas:

  • Basic support (basic types, error handling, timer).
    • Definitions for single precision (sp), double precision (dp) kinds
    • Definition of positive and negative infinite
    • A timer, with a simple interface
  • Manipulation of strings is provided in two different "flavors". Both modules provide similar functionality, and where posible they are implemented in the same code. It consists of:
    • A framework to work in a "procedural" approach is provided.
    • An "object-oriented" framework with a new type (fStr()) to represent strings.

General support

For numerical work we need to use specific minimal precision which is machine-and-compiler-independent. The real type definitions emphatize this. The names are sp and dp, and are chosen only by tradition/historic reasons. For double precision we define types with at least 15 decimal places and exponent range of 307. (see for instance http://fortranwiki.org/fortran/show/Real+precision)

It is convenient for several routines to have a symbol indicating infinite values. We define nf_minf and nf_inf for negative and positive infinite, respectively.

Constants

There are defined some commonly used constants (where the definition of "commonly" is quite arbitrary).

Name Value
Zero 0._dp
nf_minf -infinite
nf_inf infinite
C0 (0._dp, 0._dp)
C1 (1._dp, 0._dp)
CI (0._dp, 1._dp)
M_pi 3.14159265358979323846264338328_dp
M_dpi 6.28318530717958647692528676656_dp
M_pi_2 1.57079632679489661923132169164_dp
M_pi_4 0.78539816339744830966156608458_dp
M_sqrtpi 1.77245385090551602729816748334_dp
M_2_sqrtpi 1.12837916709551257389615890312_dp
M_1_pi 0.31830988618379067153776752675_dp
M_2_pi 0.63661977236758134307553505349_dp
M_lnpi 1.14472988584940017414342735135_dp
M_ln2 0.693147180559945309417232121458176568_dp
M_e 2.71828182845904523536028747135_dp
M_log2e 1.442695040888963407359924681001892137_dp
M_log10e 0.43429448190325182765112891892_dp

There are also defined many Physical constants, which are mostly those defined in scipy.constants.

Simple Timing

We implemented a simple timer that may be use by calling the methods start(), stop() and show(), as shown in the following example:

program ex_timer
USE numfor, only: dp, timer
implicit none
integer, parameter :: N = 100000
real(dp), dimension(N) :: a, b, c
integer :: i, j
type(timer) T1
call t1%start() ! Start the timer
! ---- Do your operations ....
a = 1._dp
c = 0
do j = 1, 2
do i = 1, n
b(i) = (-1)**i * a(i)
end do
do i = 1, n
c = c + b(i)
end do
c = -c
end do
! -----------------------------
call t1%stop() ! Stop the timer
call t1%show() ! Show the results
end program ex_timer

Prints:

cpu time: 7.77s
Total time: 7 s 801ms

Warning and error messages

There is currently only one routine implemented for warning and error messages, print_msg(), that may be called to produce informative results to stdout or an alternative place (usually an already opened file), and optionally stop the program if the error code is positive.

For instance,

USE numfor, only: print_msg
! Do something ...
! and after an unexpected result
call print_msg("Result outside of expected range!")

will print the message and keep the program running, while

USE numfor, only: print_msg
call print_msg("Invalid argument", "my-routine", 1)

will print the message, the routine and stop the program with error code 1.

Strings

The module strings provides routines for string manipulation. It provides some functionality similar to that available to Python strings:

  • str(): Cast numbers to strings

There are several functions that act similarly to Python string methods:

  • upper(): Converts a string to uppercase
  • lower(): Converts a string to lowercase
  • swapcase(): Swap cases: (lower -> upper) and (upper -> lower)
  • reverse(): Reverses a string "abc" -> "cba"
  • endswith(): Test wheter a string ends with a given suffix
  • startswith(): Test wheter a string starts with a given prefix
  • lstrip(): Removes leading characters of a string
  • rstrip(): Removes trailing characters of a string
  • strip(): Removes leading and trailing characters of a string
  • count_sub() Counts how many times a substring is in a string
  • find(): Finds a substring in a string
  • issub(): Test whether a substring is in a string
  • count_sub(): Counts the number of occurrences of a substring in a string
  • lstrip(): Removes leading chars in a string
  • rstrip(): Removes trailing chars in a string
  • strip(): Removes leading and trailing chars in a string
  • ljust(): Left-padds a string with a char
  • rjust(): Right-padds a string with a char
  • zfill(): Padds with zeroes a string
  • center(): Centers a string in a line
  • find(): Finds a substring in a string
  • replace(): Replaces a substring with other in a string

Examples of use

A simple example

This is a very simple example, of how to create (output) filenames from the parameters of our problem, showcasing only a few functions.

program example_strings1
USE numfor, only: str, lower, center
implicit none
character(len=1024) :: dir
character(len=:), allocatable :: rfname
character(len=:), allocatable :: output
character(len=:), allocatable :: title
real(8) :: param1
real(8) :: param2
integer :: param3
integer, parameter :: Nmax = 5
integer :: cnt
integer :: width
print *, 'Enter directory name'
! We can just set the output folder it
! dir = 'out'
! Or we could read the path from standard input.
read (*, '(A)') dir
dir = trim(dir)//'/' ! Add separator
width = 40 + len(trim(dir)) ! Used for centering
title = ' These will the files created: '
! Set some parameters
param1 = 1.3_8
param2 = 0.07_8
param3 = 42
! rfname holds the root of the filename
rfname = "out"//str(param1)//'cm'//str(param3)//'eV_'
print *, ''
print '(A)', center(title, width, '=')
do cnt = 1, nmax
! Create full filename
! Here we have to use trim because dir is of fixed length
output = lower(trim(dir))//rfname//str(cnt)//'.dat'
print '(A)', center(output, width)
end do
end program example_strings1

Note here the use of strip() refers to the above function.

After compiling and executing: Output for a very long directory name:

Enter directory name
VeryLongDirectoryNamewithCamelCaseName
========================= These will the files created:========================
verylongdirectorynamewithcamelcasename/out1.3cm42eV_1.dat
verylongdirectorynamewithcamelcasename/out1.3cm42eV_2.dat
verylongdirectorynamewithcamelcasename/out1.3cm42eV_3.dat
verylongdirectorynamewithcamelcasename/out1.3cm42eV_4.dat
verylongdirectorynamewithcamelcasename/out1.3cm42eV_5.dat

while for a short directory name the output is:

Enter directory name
dd
======= These will the files created:======
dd/out1.3cm42eV_1.dat
dd/out1.3cm42eV_2.dat
dd/out1.3cm42eV_3.dat
dd/out1.3cm42eV_4.dat
dd/out1.3cm42eV_5.dat

Object oriented strings fstring

This module provides an object-oriented version of the module strings, with functionality similar to that available to Python strings:

  • fStr(): Create type and also cast numbers to strings (of type fStr)
  • Strings may be read and write with default format
  • Multiplication by integers produce repetition
  • They can be compared for equality (or not)

The use of this module is rather simple: we define a variable of the type fStr, and then use its methods.

Examples of use

A minimal example would be simply:

USE numfor, only: fstr
type(fStr) :: s1
type(fStr) :: s2
s1 = ' Variable s1 = '
s2 = fstr(1.2123)
print *, 'The '//s1%strip(' =')//' value is ' // s2

which outputs:

"The Variable s1 value is 1.21229994"

Note here that the method strip() removes all leading and trailing spaces and occurrences of the equal symbol "=".

A simple example

The Object Oriented version of examples presented in strings (ex_strings1) has a very similar form:

program example_fstring1
USE numfor, only: fstr, len
implicit none
type(fStr) :: dir
type(fStr) :: rfname
type(fStr) :: output
type(fStr) :: title
real(8) :: param1
real(8) :: param2
integer :: param3
integer, parameter :: Nmax = 5
integer :: cnt
integer :: width
print *, 'Enter directory name'
! We can just set the output folder it
! dir = 'out'
! Or we could read the path from standard input.
read (*, *) dir
dir = dir//'/' ! Add separator
width = 30 + len(dir) ! Used for centering
title = ' These will be the files created: '
! Set some parameters
param1 = 1.3_8
param2 = 0.07_8
param3 = 42
! rfname holds the root of the filename
rfname = "out"//fstr(param1)//'cm'//fstr(param3)//'eV_'
print *, ''
print *, dir%lower()
print *, title%center(width, '=')
do cnt = 1, nmax
! Create full filename
! Here we have to use trim because dir is of fixed length
output = dir%lower()//rfname//fstr(cnt)//'.dat'
print *, output%center(width)
end do
end program example_fstring1

which, after compiling and executing, gives exactly the same result that ex_strings1 from module strings