.. | ||
Config_Fortran | ||
.gitignore | ||
benzene.xyz | ||
example_1_input.cfg | ||
example_1_output.cfg | ||
example_1_output.md | ||
example_1.f90 | ||
example_2_input.cfg | ||
example_2.f90 | ||
LICENSE | ||
m_config.f90 | ||
Makefile | ||
README.md |
config_fortran
A configuration file parser for Fortran. The intended usage is as follows:
- You create your configuration variables, by providing a default value and a description.
- You read in a text file in which new values are specified for (some of) the variables.
- You use the updated values in your program, so that there is no need to recompile.
Steps 1 and 2 can also be reversed, so that you read in the configuration files before specifying the variables. Variables can be of type integer, real, logical/bool, or string, and they can also be an array of such types.
Example
Suppose you want to use a grid of size n_grid
, then you
could do:
integer :: n_grid
type(CFG_t) :: my_cfg
call CFG_add(my_cfg, "grid_size", 1024, "Size of the grid")
call CFG_read_file(my_cfg, "my_input_file.txt")
call CFG_get(my_cfg, "grid_size", n_grid)
Here, the default grid size will be 1024. If the file
my_input_file.txt
contains a line
grid_size = 512
the actual grid size used in your program will be 512. It is also
possible to read the file first, and to combine the add
and
the get
:
integer :: n_grid = 1024
type(CFG_t) :: my_cfg
call CFG_read_file(my_cfg, "my_input_file.txt")
call CFG_add_get(my_cfg, "grid_size", n_grid, "Size of the grid")
When parsing the input file, the variable n_grid
will be
stored as plain text, since its type is not yet known. The call
CFG_add_get
converts it to the right type. The files
example_1.f90
and example_2.f90
provide
further usage examples.
The current configuration can be stored in a file with
CFG_write
, which can then be used as input again. It is
also possible to write markdown files with
CFG_write_markdown
. Writing to the special file name
"stdout"
causes the configuration to be printed to the
screen. By specifying the optional argument
hide_unused=.true.
, only the variables whose value was used
through a CFG_get
(or CFG_add_get
) are
included.
Command line arguments
A routine CFG_update_from_arguments
is included, which
parses command line arguments. Currently, two types of arguments are
supported, as shown in the examples below.
# Read in two configuration files
./my_program config_1.cfg config_2.cfg
# Read in two variables
./my_program -var_1=value -var_2=value
# Read in an array of variables
./my_program -var_2='value value'
# Mix the above options
./my_program config_1.cfg config_2.cfg -var_1=value -var_2=value
Note that variable specifications should be preceded by a dash
(-
).
Configuration file syntax
There are different types of lines:
- Blank lines, or lines only containing a comment (
# ...
or; ...
), which are ignored. - Lines indicating the start of a category:
[category_name]
- Lines with an
=
-sign. If they are part of a user-defined category, they should start with an indent. - Lines with a
+=
sign. For a scalar string variable, this will append to the string. On an array, this will append an element to the array. On other types of variables, this operation gives an error.
An example of a configuration file is shown below
age = 29
name = John
[weather]
temperature = 25.2
humidity = 23.5
happy = .true.
weather%temperature = 23.9
Note that temperature
and humidity
are
indented, and that happy
is not, which means that
happy
is not part of weather (it is in the default unnamed
category). At least two spaces or a tab counts as indentation. Outside
an indented [weather]
group, you can directly refer to its
members by using e.g. weather%temperature
, as is done on
the last line. To place variables in a category, you add them like
this:
call CFG_add(my_cfg, "weather%temperature", 25.0_dp, "The temperature")
Variables can also be arrays:
name_of_variable = value1 [value2 value3 ...] # Optional comment
The extra values [value2 value3 ...]
are omitted for a
scalar variable. You can create variables of varying array size, by
specifying dynamic_size=.true.
when creating a config
variable:
call CFG_add(my_cfg, "numbers", [1, 2], "Comment", dynamic_size=.true.)
Methods
CFG_add
: Add a variable to the configurationCFG_get
: Get the value of a variableCFG_add_get
: FirstCFG_add
, thenCFG_get
CFG_check
: Check whether all variables read from files have been defined. This is automatically performed onCFG_write
andCFG_write_markdown
.CFG_get_size
: Get the array size of a variableCFG_get_type
: Get the type of a variableCFG_sort
: Sort the configuration (for faster lookup when there are many variables)CFG_write
: Write the configuration to a standard text/config file, which can be read in again. By default, only the variables that were used are printed.CFG_write_markdown
: Write the configuration to a file in markdown formatCFG_read_file
: Read in a configuration fileCFG_update_from_arguments
: Read in the program’s arguments as configuration files.CFG_clear
: Clear config for reuse
Requirements
A modern Fortran compiler that supports Fortran 2008. The included
Makefile
was written for gfortran
(the
default) and ifort
, which you can enable by typing
make F90=ifort
.
Comparison to Fortran namelists
Benefits of config_fortran:
- You can read in (1D) arrays of unknown size
- Settings have documentation, and you can write “documented” output in text or markdown format
- If you don’t want to use global variables, you have to open and read namelists in each module that requires parameters. I think it’s nicer to read in a config_fortran type object once and pass that to the modules
- You can spread out settings over multiple files, which is convenient for setting up parameter studies (this can be done with namelists, but it’s not trivial)
- Flexibility: although namelist implementations slightly differ, you cannot change them like you can config_fortran. Config_fortran for example allows to write only those settings that have been requested in a program.
Benefits of namelist format:
- More standard, although not completely the same for different vendors/versions yet
- Support for array(3) = … syntax
- Support for array = 10*‘dummy’ syntax
(Of course, points 2 & 3 could easily be implemented in config_fortran)
Alternatives
- libconfig (C/C++)
- config4* (C/C++)
- KRACKEN (Fortran argument parser)
- FLAP (Fortran 2003+ argument parser)
- FiNeR (Fortran 2003+ config file parser)
TODO
- Write tests