Fortran's command-line parameter input made simple.
This Fortran library provides a simple framework to handle input parameters
given by command-line arguments, like ./a.out a=1 dt=0.01 method=rk45
.
These parameters can also be read from specified input files as ./a.out input.txt
.
program demo
use, intrinsic :: iso_fortran_env, only: dp => real64
use paramcard, only: paramcard_get, paramcard_summary
implicit none
integer :: a, b
real(dp) :: x, y
character(:), allocatable :: msg
call paramcard_get('a', a, 1)
call paramcard_get('b', b, 2)
call paramcard_get('x', x, 0.3_dp)
call paramcard_get('y', y, 0.4_dp)
call paramcard_get('msg', msg, '')
call paramcard_summary
print *, 'a + b = ', a + b
print *, 'x + y = ', x + y
if (msg /= '') print *, msg
end program demo
The paramcard_get
subroutine takes 3 arguments: name
, variable
and default_value
.
They specify the parameter name, the destination variable to store the parameter value
and the (optional) default value, respectively.
The paramcard_summary
subroutine prints the summary of the input parameters and
checks if there are any given parameters that are actually unused in the program.
The compiled program will accept command-line arguments like
./demo a=101 x=0.6 msg='Hello, World!'
, where parameters are overridden in the form of
name=value
. Parameter names are case- and space-insensitive.
It is also possible to pass parameters via input text files like
./demo input.txt
.
An example of an input file is as follows:
# Comment lines start with "#" or "!".
a = 101
x = 0.6
msg = Hello, World!
The output of the above example looks as follows:
a = 101 (default: 1)
b = 2
x = 0.59999999999999998 (default: 0.29999999999999999)
y = 0.40000000000000002
msg = Hello, World! (default: )
a + b = 103
x + y = 1.0000000000000000
Hello, World!
The library works with Fortran 2008 compliant compilers.
Because this is a single-file library, one can just copy a MIT-licensed file
paramcard.f90
to one's project:
curl -O https://raw.githubusercontent.com/tueda/paramcard/v0.2.3/src/paramcard.f90
Alternatively, one can use this repository as a submodule of one's Git repository:
git submodule add https://github.com/tueda/paramcard.git extern/paramcard
git -C extern/paramcard checkout v0.2.3
which makes the library source available at extern/paramcard/src/paramcard.f90
.
Integration with fpm
(v0.8.0+) is also available:
[dependencies]
paramcard = { git = "https://github.com/tueda/paramcard", tag = "v0.2.3" }
CMake (v3.14+) integration with the FetchContent module:
include(FetchContent)
FetchContent_Declare(
paramcard
GIT_REPOSITORY https://github.com/tueda/paramcard.git
GIT_TAG v0.2.3
)
FetchContent_MakeAvailable(paramcard)
if(paramcard_POPULATED)
add_library(paramcard STATIC ${paramcard_SOURCE_DIR}/src/paramcard.f90)
endif()
target_link_libraries(main PRIVATE paramcard)
Integration with CPM.cmake
:
CPMAddPackage(
NAME paramcard
GIT_REPOSITORY https://github.com/tueda/paramcard
VERSION 0.2.3
DOWNLOAD_ONLY YES
)
if (paramcard_ADDED)
add_library(paramcard STATIC ${paramcard_SOURCE_DIR}/src/paramcard.f90)
endif()
target_link_libraries(main PRIVATE paramcard)
In the paramcard
module, the following procedures are available.
Read a parameter specified by name
into variable
.
character(len=*), intent(in) :: name
character(len=:), intent(out), allocatable :: variable
character(len=*), intent(in), optional :: default_value
character(len=*), intent(in) :: name
integer(kind=int8/int16/int32/int64), intent(out) :: variable
integer(kind=int8/int16/int32/int64), intent(in), optional :: default_value
character(len=*), intent(in) :: name
real(kind=real32/real64), intent(out) :: variable
real(kind=real32/real64), intent(in), optional :: default_value
name
: the name of the parameter.variable
: the variable to store the parameter value.default_value
: the value to be used when the specified parameter is not defined from the input. If the default value is not given, then an undefined parameter leads to an error.
Set (overwrite) a parameter specified by name
as value
for later use.
character(len=*), intent(in) :: name
character(len=*), intent(in) :: value
logical, intent(in), optional :: consumed
character(len=*), intent(in) :: name
integer(kind=int8/int16/int32/int64), intent(in) :: value
logical, intent(in), optional :: consumed
character(len=*), intent(in) :: name
integer(kind=real32/real64), intent(in) :: value
logical, intent(in), optional :: consumed
name
: the name of the parameter.value
: the value to be associated with the parameter.consumed
: whether this parameter should be considered consumed (used) or not. (Default:.true.
)
Set a parameter by parsing a string containing NAME = VALUE
.
character(len=*), intent(in) :: str
str
: the string to be parsed.
Print the summary of input parameters.
integer, optional, intent(in) :: unit
logical, optional, intent(in) :: only_changed
logical, optional, intent(in) :: show_default
logical, optional, intent(in) :: check_unused
character(len=*), optional, intent(in) :: prefix
unit
: the unit number for the output. (Default:output_unit
)only_changed
: print only parameters that are changed from those default values. (Default:.false.
)show_default
: print the default values. (Default:.true.
)check_unused
: check unused (not-consumed) parameters and raise an error if found. (Default:.true.
)prefix
: the prefix to each line in the output. (Default:''
)
Perform a string formatting (interpolation) operation.
character(len=*), intent(in) :: fmt
character(len=:), allocatable :: result
fmt
: the format to be used. Parameters are substituted by braces, for example,'I have {n} apples.'
- Return
result
: the formatted string.
Write output to a file with a format.
The output file name and the format are specified by parameters.
The output is constructed by using paramcard_format
.
This subroutine does nothing if the file name or the format is empty.
character(len=*), intent(in), optional :: file_param
character(len=*), intent(in), optional :: format_param
file_param
: the parameter to specify the output file. (Default:'output_file'
)format_param
: the parameter to specify the output format. (Default:'output_format'
)
We also provide old-fashioned procedures (with some limitations) that can be used without using the module.
PROGRAM DEMO77
INTEGER A, B
DOUBLE PRECISION X, Y
CHARACTER*40 MSG
C
CALL PARAMCARD_GET_I('A', A, 1)
CALL PARAMCARD_GET_I('B', B, 2)
CALL PARAMCARD_GET_D('X', X, 0.3D0)
CALL PARAMCARD_GET_D('Y', Y, 0.4D0)
CALL PARAMCARD_GET_S('MSG', MSG, '')
CALL PARAMCARD_SUMMARY
C
WRITE (*,*) 'A + B = ', A + B
WRITE (*,*) 'X + Y = ', X + Y
IF (MSG .NE. '') WRITE (*,*) MSG
C
STOP
END
character(len=*), intent(in) :: name
character(len=*), intent(out) :: variable
character(len=*), intent(in) :: default_value
character(len=*), intent(in) :: name
integer, intent(out) :: variable
integer, intent(in) :: default_value
character(len=*), intent(in) :: name
real, intent(out) :: variable
real, intent(in) :: default_value
character(len=*), intent(in) :: name
double precision, intent(out) :: variable
double precision, intent(in) :: default_value
character(len=*), intent(in) :: name
character(len=*), intent(in) :: value
character(len=*), intent(in) :: name
integer, intent(in) :: value
character(len=*), intent(in) :: name
real, intent(in) :: value
character(len=*), intent(in) :: name
double precision, intent(in) :: value
character(len=*), intent(in) :: file_param
character(len=*), intent(in) :: format_param
brew install ford gcc git lcov pre-commit python fortran-lang/fortran/fpm
pre-commit install
pre-commit run --all-file # linter, formatter and preprocessor
fpm test # testing
GCOV=gcov-12 ./scripts/gen-coverage.sh # coverage report
ford API-doc-FORD-file.md # documentation