FORCES library
This is the FORTRAN library for Computational Environmental Systems of the
Department Computational Hydrosystems
Helmholtz Centre for Environmental Research - UFZ
Permoserstr. 15
04318 Leipzig, Germany
It is a lightweight fork of the jams_fortran library maintained by Matthias Cuntz et al: https://github.com/mcuntz/jams_fortran
The jams_fortran library was formerly developed at the CHS department at the UFZ and is now released under the MIT license.
Using FORCES with your Fortran program
Let's assume, you want to write a fortran program using forces, like this example test.f90:
program test
use mo_message, only : message
implicit none
call message("This is working!")
end program testYou should create a minimal CMakeLists.txt file next to the test.f90 file like this:
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
# get CPM (package manager)
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_0.35.0.cmake")
file(DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.35.0/CPM.cmake ${CPM_DOWNLOAD_LOCATION})
include(${CPM_DOWNLOAD_LOCATION})
# create project
project(MyProject LANGUAGES Fortran)
# add executable
add_executable(test test.f90)
# add FORCES dependency
CPMAddPackage("https://git.ufz.de/chs/forces.git@0.3.1")
# link dependencies
target_link_libraries(test forces)There, CPM (the cmake package manager) is downloaded on the fly and used to get FORCES to be able to link against it.
Afterwards you only need to do the following to configure, compile and execute your program:
cmake -B build
cmake --build build --parallel
./build/testAnd it will happily write:
This is working!
If you have the FORCES sources downloaded and you want to link a local executable against it, you can specify a path to CMake with FORCES_EXE:
cmake -B build -DFORCES_EXE=test.f90
cmake --build build --parallel
./build/mainYou can use this with the examples provided in the examples/ directory, e.g. FORCES_EXE=examples/01_grids/01_regridding.f90.
The executable will be always called main.
Build Configuration
FORCES uses standard CMake inputs such as CMAKE_BUILD_TYPE and a small set of project-specific cache options:
FORCES_BUILD_TESTING: build the FORCES pfUnit tests. Defaults toOFF.FORCES_WITH_COVERAGE: enable GNU coverage instrumentation for FORCES test builds.FORCES_WITH_OpenMP: enable OpenMP support.FORCES_WITH_MPI: enable MPI support.FORCES_WITH_NETCDF: enable NetCDF support.FORCES_WITH_OPTIMIZATION: include optimization routines.FORCES_ENABLE_NATIVE: enable host-native tuning forReleaseandRelWithDebInfobuilds.FORCES_EXE: build a local executable linked againstforces.
Typical configure commands:
# test build
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DFORCES_BUILD_TESTING=ON
# OpenMP build
cmake -B build -DCMAKE_BUILD_TYPE=Release -DFORCES_BUILD_TESTING=ON -DFORCES_WITH_OpenMP=ON
# GNU coverage build
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DFORCES_BUILD_TESTING=ON -DFORCES_WITH_COVERAGE=ON
# local executable build
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DFORCES_EXE=test.f90FORCES_WITH_COVERAGE is only meaningful for test builds and requires GNU Fortran.
Dependencies and Requirements
- Fortran compiler: We support gfortran, nagfor and ifort
- Build system: We support make and ninja
- cmake: Software for build automation
- NetCDF-C: NetCDF I/O library
- (optional) fypp: Fortran pre-processor written in Python
It is recommended to have a clean installation at a custom location
for a C compiler, a Fortran compiler and the NetCDF C library with consistent compilers.
We recommend to use a conda environment by using Miniconda to get all dependencies easily:
conda create -y --prefix ./forces_env
conda activate ./forces_env
conda config --add channels conda-forge
conda config --set channel_priority strict
conda install -y cmake make fortran-compiler libnetcdf fyppWith this you could now proceed with the example given above.
Testing with pFUnit
FORCES uses pFUnit for its unit tests. When FORCES_BUILD_TESTING=ON, CMake will look for PFUNIT. If pFUnit is not available, configuration still succeeds, but no FORCES tests are added.
You need to provide pFUnit to run the tests. It is unfortunately not available on conda yet. To compile it by hand in the current conda environment, you can do the following:
conda install -y m4
git clone https://github.com/Goddard-Fortran-Ecosystem/pFUnit.git
cmake -DCMAKE_BUILD_TYPE=Release -B pFUnit/build -S pFUnit
cmake --build pFUnit/build --parallel
cmake --install pFUnit/build --prefix $CONDA_PREFIX
rm -rf pFUnitThen you can compile and run the tests and/or calculate coverage in the same environment:
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DFORCES_BUILD_TESTING=ON -DFORCES_WITH_COVERAGE=ON
cmake --build build --parallel
cmake --build build --target test
cmake --build build --target coverageRun all configured tests with:
ctest --test-dir build --output-on-failureRun a single pFUnit suite with ctest -R, for example:
ctest --test-dir build --output-on-failure -R test_mo_messageIf you only want to configure a subset of the pFUnit files, use FORCES_TEST_GLOB:
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DFORCES_BUILD_TESTING=ON -DFORCES_TEST_GLOB='test_mo_message.pf'This is useful when iterating on one module or when you want to avoid configuring optional suites such as NetCDF- or optimization-related tests.
The coverage target additionally requires lcov to be available on PATH.
For a more complex project, prepared for unit-tests, documentation and modules, have a look at the Fortran Template.
License
LGPLv3 (c) 2005-2026 CHS-Developers
Changelog for v0.9
- See the git diff for details.
Enhancements and Changes
-
mo_netcdf,mo_ncread,mo_ncwrite,mo_netcdf_wrapper(130, 132)- removed the
netcdf-fortrandependency and replaced it with a FORCES-owned wrapper built directly on top ofnetcdf-c - kept the existing high-level FORCES NetCDF API intact while moving production code away from
use netcdf - preserved FORCES NetCDF semantics such as 1-based slicing indices and current dimension ordering
- added a small C companion layer for selected
netcdf-cfunctionality - added 64-bit NetCDF dimension/index support through additive
*64methods NcGroupnow providessetDimension64NcDimensionnow providesgetLength64NcVariablenow providesgetShape64,setData64,getData64, andreadInto64- tightened wrapper-side validation and contiguity requirements for rank>0 buffers passed via
c_loc
- removed the
-
mo_grid,mo_spatial_index,mo_grid_scaler,mo_grid_helper,mo_grid_constants(131, 136, 137, 139, 140)- added
grid_t%closest_cell_id_by_axesfor fast nearest-cell lookup on axis-aligned grids - reworked
grid_closest_cell_id_by_axesto avoid large local/thread-private temporaries that caused failures on huge grids with OpenMP - added a reusable KD-tree based spatial index for Cartesian and spherical coordinates
- added exact and batched nearest-active-cell search support for large and irregular grids
- added
grid_t%build_spatial_index - added
grid_t%in_cell(i,j,x,y,aux)for regular and auxiliary-grid containment checks - added
grid_t%is_matching(other[, tol, aux])to compare grids - added a dedicated
nearest_regridder_tfor arbitrary nearest-neighbor remapping - target-mask derivation for nearest-neighbor remapping is now based on source-cell containment
- refactored grid-independent helpers into
mo_grid_helper - added shared grid selector constants in
mo_grid_constants - restored compatibility-facing exports in
mo_gridfordata_t,mask_from_var, anddata_from_var - fixed coarse-weight calculation in the scaler so empty coarse cells no longer trigger division-by-zero
- added
-
mo_grid_scaler,mo_orderpack(127, 138)- added a new
up_medianupscaling operator - median upscaling is available for floating-point output and supports both
dp -> dpandi4 -> dp - made
mo_orderpackthread-safe by removing global state from median-related routines - marked the affected routines as
recursivefor safer OpenMP use on older compilers
- added a new
-
mo_grid_io,mo_grid(108, 113, 118, 123, 126, 128)- added
ref_timeto input and output datasets for unit reference - clarified
start_timesemantics so it now represents the start of the time frame in the file - made the optional
timestamphandling consistent ininput_dataset%initand related routines - passed tolerance from initialization down to the respective functions
input%time_indexnow returns0when the requested time matches the file start time, preventing out-of-bounds chunk reads- added restart-oriented grid I/O via
grid_t%to_restartandgrid_t%from_restart - restart files can now include additional grid information for faster setup
- removed
areaandmaskarguments fromto_netcdf - added
allow_statictotype(var)so temporal inputs can optionally accept static variables on read-in - kept
staticas the authoritative flag for output handling and strict validation - added optional NetCDF output controls
cache_sizeandstatic_contiguous - when
cache_sizeis set, output datasets derive chunk sizes from the cache budget and pass chunk/cache settings to NetCDF - static output variables can now optionally be written as contiguous storage
- added
-
mo_dag(104)- added
n_roots,n_leaves,roots, andleavesmethods to the DAG type - updated
traverseso traversal starts at leaves when traversing down and at roots otherwise
- added
-
mo_message,mo_logging(120, 125)- added
mo_message::use_logto route messages through the logging module - added
log_redirect_to_file(log_file_path[, append])to redirect log output to a file
- added
-
mo_errormeasures(141)- added Spatial Efficiency (SPAEF) and Classification Accuracy (CA) metrics
- added support for the new objective functions 35 to 47 based on SPAEF and CA
-
CI / CMake / compiler support (134, 143, 144)
- fixed NAG+OpenMP support
- adjusted atomic capture usage for NAG requirements
- fixed OpenMP link handling
- increased the minimum CMake version to 3.18 for the required
LINK_LANGUAGEgenerator expression - added
FORCES_ENABLE_NATIVECMake option to enable HPC/local optimized code - added
RelWithDebInfobuild support for faster run/debug cycles with backtraces and debug logging - added CI test jobs for NAG v7.2 on DS3
- added CI test jobs for GNU v13.3 on DS3
- moved build configuration to FORCES-owned top-level cache options such as
FORCES_BUILD_TESTING,FORCES_WITH_COVERAGE,FORCES_WITH_NETCDF,FORCES_WITH_MPI,FORCES_WITH_OpenMP,FORCES_WITH_OPTIMIZATION, andFORCES_ENABLE_NATIVE - made testing opt-in through
FORCES_BUILD_TESTING - switched coverage setup to target-scoped coverage flags with GNU-only validation at configure time
- updated the vendored
cmake/helper subtree tocmake-fortran-scriptsv3.0 - updated
README.md,cmake/README.md, and.gitlab-ci.ymlto document and use the newFORCES_*options - changed the OpenMP link on
forcesfromPUBLICtoPRIVATE, so downstream targets no longer inherit OpenMP compile flags implicitly - kept pFUnit discovery non-fatal and quiet when tests are enabled but
PFUNITis not installed - removed
-fp-model=preciseand-fprotect-parensfor Intel release builds to allow better optimization - added
-fcheck=no-array-tempsfor GNU debug builds to suppress excessive runtime warnings for array temporaries - adjusted CMake+NAG+OpenMP handling by only adding
-openmpand removing-glinein debug mode
Fixes
-
mo_glob(121)- fixed matching of empty strings against non-trivial glob patterns
- empty text is now handled separately to avoid substring bound errors
-
mo_dag(115)- fixed OpenMP atomic capture statements that did not follow the OpenMP rules in the updated DAG traversal code
-
tests / numerical robustness (143)
- updated tests to be less dependent on exact floating-point optimizer trajectories
- improved tolerance-based floating-point comparisons in
mo_corr,mo_percentile, andmo_errormeasures - removed non-portable subnormal relational assertions in
mo_utils
-
mo_percentile,mo_grid(143)- fixed a mode-3 index-selection issue in
mo_percentilethat could flip the selected index under aggressive Intel optimization in single precision - fixed
grid_map_longitude_for_domainso periodic upper-bound ties no longer rely onmodulo(..., 360)
- fixed a mode-3 index-selection issue in
