diff --git a/CITATION b/CITATION
index 8105be9996fa488f1dfde73a8a5bb12a7adb6c88..2073289a03b108f309171e4ec9f72a1219127ccb 100644
--- a/CITATION
+++ b/CITATION
@@ -1,14 +1,14 @@
 If you use the Climate Data Operators (CDO) to process data for an article in a scientific publication, please cite:
 
-Schulzweida, Uwe. (2022). CDO User Guide (2.1.0). Zenodo. https://doi.org/10.5281/zenodo.7112925
+Schulzweida, Uwe. (2023). CDO User Guide (2.3.0). Zenodo. https://doi.org/10.5281/zenodo.10020800
 
-@misc{schulzweida_uwe_2022_7112925,
+@misc{schulzweida_2023_10020800,
   author       = {Schulzweida, Uwe},
   title        = {CDO User Guide},
   month        = oct,
-  year         = 2022,
+  year         = 2023,
   publisher    = {Zenodo},
-  version      = {2.1.0},
-  doi          = {10.5281/zenodo.7112925},
-  url          = {https://doi.org/10.5281/zenodo.7112925}
-}
\ No newline at end of file
+  version      = {2.3.0},
+  doi          = {10.5281/zenodo.10020800},
+  url          = {https://doi.org/10.5281/zenodo.10020800}
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b5710d59f73624034568008ef561b3dae62b90c5
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,33 @@
+# cmake options:
+#
+#       -DCMAKE_INSTALL_PREFIX=/path/to/install
+
+cmake_minimum_required( VERSION 3.12 FATAL_ERROR )
+
+project( cdo VERSION 2.4.0 LANGUAGES C CXX )
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 20)
+
+configure_file (
+  "${PROJECT_SOURCE_DIR}/cmake/cdo_config.h.in"
+  "${PROJECT_BINARY_DIR}/config.h"
+  )
+
+include_directories("${PROJECT_BINARY_DIR}")
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+find_package(NetCDF)
+
+set(HAVE_NETCDF ${netCDF_FOUND} )
+
+if (netCDF_FOUND)
+  set(HAVE_NETCDF 1)
+endif ()
+
+add_subdirectory( libcdi/src )
+add_subdirectory( src/lib/yac )
+add_subdirectory( src/lib/gradsdes )
+add_subdirectory( src/lib/healpix )
+add_subdirectory( src )
+#target_link_libraries(cdolib PRIVATE NetCDF)
diff --git a/ChangeLog b/ChangeLog
index a99c828c6fa88570916b4163a867a6bc3a8ee954..246830ee911d12a1e1d0cd4a65a2b17e7f452b19 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,58 @@
-2023-11-29  Uwe Schulzweida
+2024-02-22  Uwe Schulzweida
+
+	* Using CDI library version 2.4.0
+	* Using YAC version 3.1.0
+	* Version 2.4.0 release
+
+2024-02-21  Uwe Schulzweida
+
+	* setgridtype,lonlat: Converts a regular lonlat grid stored as projection back to a lonlat grid
+
+2024-02-20  Uwe Schulzweida
+
+	* showattribute: print special global attributes uuidOfHGrid and grid_file_uri
+
+2024-02-13  Uwe Schulzweida
+
+	* expr: var statement failed (bug fix)
+
+2024-01-23  Uwe Schulzweida
+
+	* Arith: fill mode for infile2 doesn't work with pipes [Bug #11733]
+
+2024-01-16  Uwe Schulzweida
+
+        * after: change computation of geopotential height from full to half levels [Bug #11346]
+
+2024-01-16  Uwe Schulzweida
+
+	* rotated pole mapping failed with negative north_pole_grid_longitude attribute [Bug #11661]
+
+2024-01-15  Uwe Schulzweida
+
+	* select: allow negative numbers for parameter levidx to select level indices from the end
+
+2024-01-14  Uwe Schulzweida
+
+	* Vertintml: wrong result with data on model half-levels (bug fix)
+
+2024-01-12  Uwe Schulzweida
+
+	* New operator: gheight_half - geopotential height on model half-levels
+
+2024-01-05  Uwe Schulzweida
+
+	* timcor: add pvalue to output, if input has only one field
+
+2023-10-28  Uwe Schulzweida
+
+	* gradsdes: fix integer overflow in map file
+
+2023-10-27  Uwe Schulzweida
+
+	* pack: add support to read pack parameters from file
+
+2023-11-07  Uwe Schulzweida
 
 	* Using CDI library version 2.3.1
 	* Version 2.3.1 release
diff --git a/NEWS b/NEWS
index d6711f14781974cb32f3af00b83d8b42d0e8e776..d57123914b77e3f770a3b261abd77b3cf7f0482b 100644
--- a/NEWS
+++ b/NEWS
@@ -3,12 +3,25 @@ CDO NEWS
 
 Improvement
 
-Version 2.4.0 (21 October 2024):
+Version 2.4.0 (22 Feb 2024):
 
    New features:
+     * Changed to C++20
+     * Add FDB (Fields DataBase) support (status: experimental)
      * Remapweights: Use environment variable REMAP_MAP3D=1 to generate all mapfiles of the first 3D field with variing masks
+     * pack: add support to read pack parameters from file
+     * select: allow negative numbers for parameter levidx to select level indices from the end
    New operators:
+     * New operator: pressure - pressure on full-levels
+     * New operator: pressure_half - pressure on half-levels
+     * New operator: delta_pressure - pressure difference of half-levels
+     * New operator: gheight_half - geopotential height on half-levels
    Fixed bugs:
+     * after: change computation of geopotential height from full to half levels [Bug #11346]
+     * expr: var statement failed
+     * gradsdes: fix integer overflow in map file
+     * Arith: fill mode for infile2 doesn't work with pipes [Bug #11733]
+     * rotated pole mapping failed with negative north_pole_grid_longitude attribute [Bug #11661]
 
 Version 2.3.1 (29 November 2023):
 
diff --git a/OPERATORS b/OPERATORS
index 53d31f2e272528a9bd4923a61b0536bf65c358ee..129dee6f7ea4e41ada682e99fef6082039a9d662 100644
--- a/OPERATORS
+++ b/OPERATORS
@@ -649,8 +649,12 @@ Operator catalog:
    Rotuv         rotuvb          Backward rotation
    Mrotuvb       mrotuvb         Backward rotation of MPIOM data
    Mastrfu       mastrfu         Mass stream function
+   Pressure      pressure_half   Pressure on half-levels
+   Pressure      pressure        Pressure on full-levels
+   Pressure      delta_pressure  Pressure difference of half-levels
    Derivepar     sealevelpressure Sea level pressure
-   Derivepar     gheight         Geopotential height
+   Derivepar     gheight         Geopotential height on full-levels
+   Derivepar     gheight_half    Geopotential height on half-levels
    Adisit        adisit          Potential temperature to in-situ temperature
    Adisit        adipot          In-situ temperature to potential temperature
    Rhopot        rhopot          Calculates potential density
@@ -694,6 +698,7 @@ Operator catalog:
    EcaCfd        eca_cfd         Consecutive frost days index per time period
    EcaCsu        eca_csu         Consecutive summer days index per time period
    EcaCwd        eca_cwd         Consecutive wet days index per time period
+   EcaCwd        etccdi_cwd      Consecutive wet days index per time period
    EcaCwdi       eca_cwdi        Cold wave duration index wrt mean of reference period
    EcaCwfi       eca_cwfi        Cold-spell days index wrt 10th percentile of reference period
    EcaCwfi       etccdi_csdi     Cold-spell duration index
@@ -704,6 +709,7 @@ Operator catalog:
    EcaHd         eca_hd          Heating degree days per time period
    EcaHwdi       eca_hwdi        Heat wave duration index wrt mean of reference period
    EcaHwfi       eca_hwfi        Warm spell days index wrt 90th percentile of reference period
+   EcaHwfi       etccdi_wsdi     Warm Spell Duration Index
    EcaId         eca_id          Ice days index per time period
    EcaId         etccdi_id       Ice days index per time period
    EcaR75p       eca_r75p        Moderate wet days wrt 75th percentile of reference period
diff --git a/cmake/FindNetCDF.cmake b/cmake/FindNetCDF.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..194b1a0d32a629aefc0841582c1df02f0b681083
--- /dev/null
+++ b/cmake/FindNetCDF.cmake
@@ -0,0 +1,131 @@
+#[==[
+Provides the following variables:
+
+  * `NetCDF_FOUND`: Whether NetCDF was found or not.
+  * `NetCDF_INCLUDE_DIRS`: Include directories necessary to use NetCDF.
+  * `NetCDF_LIBRARIES`: Libraries necessary to use NetCDF.
+  * `NetCDF_VERSION`: The version of NetCDF found.
+  * `NetCDF::NetCDF`: A target to use with `target_link_libraries`.
+  * `NetCDF_HAS_PARALLEL`: Whether or not NetCDF was found with parallel IO support.
+#]==]
+
+function(FindNetCDF_get_is_parallel_aware include_dir)
+  file(STRINGS "${include_dir}/netcdf_meta.h" _netcdf_lines
+    REGEX "#define[ \t]+NC_HAS_PARALLEL[ \t]")
+  string(REGEX REPLACE ".*NC_HAS_PARALLEL[ \t]*([0-1]+).*" "\\1" _netcdf_has_parallel "${_netcdf_lines}")
+  if (_netcdf_has_parallel)
+    set(NetCDF_HAS_PARALLEL TRUE PARENT_SCOPE)
+  else()
+    set(NetCDF_HAS_PARALLEL FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+# Try to find a CMake-built NetCDF.
+find_package(netCDF CONFIG QUIET)
+if (netCDF_FOUND)
+  # Forward the variables in a consistent way.
+  set(NetCDF_FOUND "${netCDF_FOUND}")
+  set(NetCDF_INCLUDE_DIRS "${netCDF_INCLUDE_DIR}")
+  set(NetCDF_LIBRARIES "${netCDF_LIBRARIES}")
+  set(NetCDF_VERSION "${NetCDFVersion}")
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(NetCDF
+    REQUIRED_VARS NetCDF_INCLUDE_DIRS NetCDF_LIBRARIES
+    VERSION_VAR NetCDF_VERSION)
+
+  if (NOT TARGET NetCDF::NetCDF)
+    add_library(NetCDF::NetCDF INTERFACE IMPORTED)
+    if (TARGET "netCDF::netcdf")
+      # 4.7.3
+      set_target_properties(NetCDF::NetCDF PROPERTIES
+        INTERFACE_LINK_LIBRARIES "netCDF::netcdf")
+    elseif (TARGET "netcdf")
+      set_target_properties(NetCDF::NetCDF PROPERTIES
+        INTERFACE_LINK_LIBRARIES "netcdf")
+    else ()
+      set_target_properties(NetCDF::NetCDF PROPERTIES
+        INTERFACE_LINK_LIBRARIES "${netCDF_LIBRARIES}")
+    endif ()
+  endif ()
+
+  FindNetCDF_get_is_parallel_aware("${NetCDF_INCLUDE_DIRS}")
+  # Skip the rest of the logic in this file.
+  return ()
+endif ()
+
+find_package(PkgConfig QUIET)
+if (PkgConfig_FOUND)
+  pkg_check_modules(_NetCDF QUIET netcdf IMPORTED_TARGET)
+  if (_NetCDF_FOUND)
+    # Forward the variables in a consistent way.
+    set(NetCDF_FOUND "${_NetCDF_FOUND}")
+    set(NetCDF_INCLUDE_DIRS "${_NetCDF_INCLUDE_DIRS}")
+    set(NetCDF_LIBRARIES "${_NetCDF_LIBRARIES}")
+    set(NetCDF_VERSION "${_NetCDF_VERSION}")
+
+    include(FindPackageHandleStandardArgs)
+    find_package_handle_standard_args(NetCDF
+      REQUIRED_VARS NetCDF_LIBRARIES
+      # This is not required because system-default include paths are not
+      # reported by `FindPkgConfig`, so this might be empty. Assume that if we
+      # have a library, the include directories are fine (if any) since
+      # PkgConfig reported that the package was found.
+      # NetCDF_INCLUDE_DIRS
+      VERSION_VAR NetCDF_VERSION)
+
+    if (NOT TARGET NetCDF::NetCDF)
+      add_library(NetCDF::NetCDF INTERFACE IMPORTED)
+      set_target_properties(NetCDF::NetCDF PROPERTIES
+        INTERFACE_LINK_LIBRARIES "PkgConfig::_NetCDF")
+    endif ()
+
+    FindNetCDF_get_is_parallel_aware("${_NetCDF_INCLUDEDIR}")
+    # Skip the rest of the logic in this file.
+    return ()
+  endif ()
+endif ()
+
+find_path(NetCDF_INCLUDE_DIR
+  NAMES netcdf.h
+  DOC "netcdf include directories")
+mark_as_advanced(NetCDF_INCLUDE_DIR)
+
+find_library(NetCDF_LIBRARY
+  NAMES netcdf
+  DOC "netcdf library")
+mark_as_advanced(NetCDF_LIBRARY)
+
+if (NetCDF_INCLUDE_DIR)
+  file(STRINGS "${NetCDF_INCLUDE_DIR}/netcdf_meta.h" _netcdf_version_lines
+    REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)")
+  string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" _netcdf_version_major "${_netcdf_version_lines}")
+  string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" _netcdf_version_minor "${_netcdf_version_lines}")
+  string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" _netcdf_version_patch "${_netcdf_version_lines}")
+  string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" _netcdf_version_note "${_netcdf_version_lines}")
+  set(NetCDF_VERSION "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}")
+  unset(_netcdf_version_major)
+  unset(_netcdf_version_minor)
+  unset(_netcdf_version_patch)
+  unset(_netcdf_version_note)
+  unset(_netcdf_version_lines)
+
+  FindNetCDF_get_is_parallel_aware("${NetCDF_INCLUDE_DIR}")
+endif ()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(NetCDF
+  REQUIRED_VARS NetCDF_LIBRARY NetCDF_INCLUDE_DIR
+  VERSION_VAR NetCDF_VERSION)
+
+if (NetCDF_FOUND)
+  set(NetCDF_INCLUDE_DIRS "${NetCDF_INCLUDE_DIR}")
+  set(NetCDF_LIBRARIES "${NetCDF_LIBRARY}")
+
+  if (NOT TARGET NetCDF::NetCDF)
+    add_library(NetCDF::NetCDF UNKNOWN IMPORTED)
+    set_target_properties(NetCDF::NetCDF PROPERTIES
+      IMPORTED_LOCATION "${NetCDF_LIBRARY}"
+      INTERFACE_INCLUDE_DIRECTORIES "${NetCDF_INCLUDE_DIR}")
+  endif ()
+endif ()
diff --git a/cmake/cdo_config.h.in b/cmake/cdo_config.h.in
new file mode 100644
index 0000000000000000000000000000000000000000..e88c2884c0989b3f2dc50872d80083084aa3d35d
--- /dev/null
+++ b/cmake/cdo_config.h.in
@@ -0,0 +1,20 @@
+#ifndef cdo_config_h
+#define cdo_config_h
+
+#define PACKAGE_NAME      "@PROJECT_NAME@"
+
+#define VERSION           "@PROJECT_VERSION@"
+
+#define CDI               1
+
+#define HAVE_LIBGRIB      1
+#define HAVE_LIBCGRIBEX   1
+#define HAVE_LIBEXTRA     1
+#define HAVE_LIBSERVICE   1
+#define HAVE_LIBIEG       1
+
+#define HAVE_NETCDF       @HAVE_NETCDF@
+#define HAVE_NETCDF_NC4   @netCDF_HAS_NC4@
+#define NETCDF_FOUND      @NetCDF_FOUND@
+
+#endif /* cdo_config_h */
diff --git a/config/default b/config/default
index 383873aafc1ef4681dcb8e54df3a7f4ed9a737e1..ed4724e917f1ca461ed85760af1ed0172ba174d2 100755
--- a/config/default
+++ b/config/default
@@ -30,20 +30,22 @@ set -x
 #
 case "${HOSTNAME}" in
 # i386-apple-darwin10
+#                 --with-magics=/opt/local "
     bailung*|d146*)
         CDOLIBS="--with-fftw3 \
-                 --with-eccodes=$HOME/local/eccodes-2.32.0 \
-                 --with-netcdf=$HOME/local/netcdf-4.9.0 \
-                 --with-hdf5=$HOME/local/hdf5-1.12.1-threadsafe \
+                 --with-fdb5=$HOME/local/ecmwf \
+                 --with-eccodes=/opt/local \
+                 --with-netcdf=/opt/local \
+                 --with-hdf5=/opt/local \
                  --with-udunits2=/opt/local \
                  --with-curl=/opt/local \
-                 --with-cmor=$HOME/local/cmor-3.6.1 \
                  --with-libxml2=/opt/local \
                  --with-ossp-uuid=/opt/local \
-                 --with-proj=/opt/local/lib/proj7 \
-                 --with-szlib=/opt/local/lib/libaec \
-                 --with-magics=/opt/local "
-        LDFLAGS="-Wl,-rpath,$HOME/local/eccodes-2.32.0/lib -L/opt/local/lib -ljson-c"
+                 --with-proj=/opt/local/lib/proj9 \
+                 --with-cmor=$HOME/local/cmor-3.6.1 \
+                 --with-szlib=/opt/local/lib/libaec"
+#        LDFLAGS="-Wl,-rpath,$HOME/local/eccodes-2.32.0/lib -L/opt/local/lib -ljson-c"
+        LDFLAGS="-Wl,-rpath,$HOME/local/ecmwf/lib -L/opt/local/lib -ljson-c"
 
         if  test "$COMP" = intel ; then
 	  ${CONFPATH}configure \
@@ -146,20 +148,20 @@ case "${HOSTNAME}" in
 	;;
     hama*)
 #                 --with-fdb5=$HOME/src/fdb \
-#                 --with-cmor=$HOME/local/cmor-3.6.1 \
         CDOLIBS="--with-fftw3 \
                  --with-curl \
                  --with-szlib=/opt/local/lib/libaec \
-                 --with-eccodes=$HOME/local/eccodes-2.32.0 \
-                 --with-netcdf=$HOME/local/netcdf-c-4.9.2 \
-                 --with-hdf5=$HOME/local/hdf5-1.12.0 \
+                 --with-eccodes=/opt/local \
+                 --with-netcdf=/opt/local \
+                 --with-hdf5=/opt/local \
                  --with-cmor=$HOME/local/cmor-3.6.1 \
                  --with-libxml2=/opt/local \
                  --with-udunits2=/opt/local \
                  --with-ossp-uuid=/opt/local \
                  --with-proj=/opt/local/lib/proj9 \
                  --with-magics=/opt/local"
-        LDFLAGS="-Wl,-rpath,$HOME/local/eccodes-2.32.0/lib -Wl,-rpath,$HOME/src/fdb/lib"
+#        LDFLAGS="-Wl,-rpath,$HOME/local/eccodes-2.32.0/lib -Wl,-rpath,$HOME/src/fdb/lib"
+        LDFLAGS=""
 
         if  test "$COMP" = intel ; then
 	  ${CONFPATH}configure \
@@ -354,12 +356,14 @@ case "${HOSTNAME}" in
 	fi
 	;;
 # levante
+#        NETCDFPATH=/sw/spack-levante/netcdf-c-main-bdxvs5
+#        HDF5PATH=/sw/spack-levante/hdf5-1.14.2-gxhi2f
     levante*)
         NETCDFPATH=/sw/spack-levante/netcdf-c-4.8.1-qk24yp
         HDF5PATH=/sw/spack-levante/hdf5-1.12.1-akf2kp
         UDUNITS2PATH=/sw/spack-levante/udunits-2.2.28-da6pla
         FFTW3PATH=/sw/spack-levante/fftw-3.3.10-fnfhvr
-        ECCODESPATH=/sw/spack-levante/eccodes-2.21.0-4ywkk4
+        ECCODESPATH=/sw/spack-levante/eccodes-2.32.5-ly6tko
         MAGICSPATH=/sw/spack-levante/magics-4.9.3-z64bdu
         SZPATH=/sw/spack-levante/libaec-1.0.5-r5sdw5
         #PROJPATH=/sw/spack-levante/proj-8.1.0-i6a6ah
diff --git a/configure.ac b/configure.ac
index a4b3764404a4792980536d8322ef8f9551d79643..10758c3928977333fe402cb0f5d42216399474fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@
 #  libtool  2.4.2
 
 AC_PREREQ([2.69])
-AC_INIT([cdo],[2.3.1],[https://mpimet.mpg.de/cdo])
+AC_INIT([cdo],[2.4.0],[https://mpimet.mpg.de/cdo])
 
 AC_DEFINE_UNQUOTED(CDO, ["$PACKAGE_VERSION"], [CDO version])
 
diff --git a/doc/tex/Modules b/doc/tex/Modules
index 681456d89ee8bbdae36580bc2d614478d08d8927..8cb6e0746403b927ee095e3035f2d44e797b4107 100644
--- a/doc/tex/Modules
+++ b/doc/tex/Modules
@@ -160,6 +160,7 @@ WindTrans     Miscellaneous
 Rotuv         Miscellaneous
 Mrotuvb       Miscellaneous
 Mastrfu       Miscellaneous
+Pressure      Miscellaneous
 Derivepar     Miscellaneous
 Adisit        Miscellaneous
 Rhopot        Miscellaneous
diff --git a/doc/tex/cdo.tex b/doc/tex/cdo.tex
index dad49f026de3c8ee5c7c9ad5fe819043d52b4569..763cdedbe86ecf72a1b00f3ac83ccb7574ae299a 100644
--- a/doc/tex/cdo.tex
+++ b/doc/tex/cdo.tex
@@ -206,7 +206,7 @@ keepaspectratio]{cdo_libdep.pdf}}%
 \end{picture}
 
 \begin{flushright}
-\large \textbf{Climate Data Operator \\ Version 2.3.1  \\ November 2023}
+\large \textbf{Climate Data Operator \\ Version 2.4.0  \\ February 2024}
 \end{flushright}
 
 \vfill
diff --git a/doc/tex/cdoprog.tex b/doc/tex/cdoprog.tex
index 6e7733c4f748dcee0b6fe43f1d8b5e16e14b9455..23b7bf54dbc7e777ba51ce9c3af9be4d9202bcfa 100644
--- a/doc/tex/cdoprog.tex
+++ b/doc/tex/cdoprog.tex
@@ -14,7 +14,7 @@
 \put(0,0.0){\line(1,0){3.95}}
 \end{picture}
 \begin{flushright}
-{\small{Climate Data Operator \\ Version 2.3.1 \\ November 2023}}
+{\small{Climate Data Operator \\ Version 2.4.0 \\ February 2024}}
 \end{flushright}
 
 \vspace*{0mm}
diff --git a/doc/tex/makedoc b/doc/tex/makedoc
index 43b1113384049f67248acd8d2b60491cd210a374..64d902a0b6df6001620578e0243a15d3e50cde9f 100755
--- a/doc/tex/makedoc
+++ b/doc/tex/makedoc
@@ -6,7 +6,8 @@ $prog       = "CDO";
 $longname   = "Climate Data Operators";
 $moddir     = "mod";
 $modules    = "Modules";
-$help       = "../../src/operator_help.h";
+$helphh     = "../../src/operator_help.h";
+$helpcc     = "../../src/operator_help.cc";
 $catalog    = "catalog.tex";
 $abclist    = "alphabetic_list.tex";
 @oplist     = ();
@@ -17,7 +18,8 @@ $class      = "cdo_class";
 #
 open(README,   ">../../OPERATORS");
 open(CDOHTML,  ">cdo_oper.html");
-open(HELPFILE, ">$help");
+open(HELPHH,   ">$helphh");
+open(HELPCC,   ">$helpcc");
 open(TCFILE,   ">$catalog");
 open(ABCLIST,  ">$abclist");
 open(TROVER,   ">$refover");
@@ -150,21 +152,27 @@ print TRCARD "\\vspace*{2mm}\n";
 print TRCARD "\\subsection*{Operators}\n";
 print TRCARD "\n";
 #
-print HELPFILE "// Automatically created with makedoc, don't edit!\n";
-print HELPFILE "\n";
-print HELPFILE "#ifndef OPERATOR_HELP_H\n";
-print HELPFILE "#define OPERATOR_HELP_H\n";
-print HELPFILE "\n";
-print HELPFILE "// clang-format off\n";
+print HELPHH "// Automatically created with makedoc, don't edit!\n";
+print HELPHH "\n";
+print HELPHH "#ifndef OPERATOR_HELP_H\n";
+print HELPHH "#define OPERATOR_HELP_H\n";
+print HELPHH "\n";
+print HELPHH "#include <vector>\n";
+print HELPHH "#include <string_view>\n";
+print HELPHH "\n";
+print HELPHH "typedef std::vector<std::string_view> CdoHelp;\n";
+print HELPHH "\n";
+#
+print HELPCC "// Automatically created with makedoc, don't edit!\n";
+print HELPCC "\n";
+print HELPCC "#include \"operator_help.h\"\n";
+print HELPCC "\n";
+print HELPCC "// clang-format off\n";
 #
 $help_print = 1;
 $oldchap = "oldchap";
 $nline = 0;
 
-print HELPFILE "\n";
-#print HELPFILE "#include <vector>\n";
-#print HELPFILE "#include <string>\n";
-
 while (<MOFILE>) {
     chomp;
     ($modulefile) = split(" ");
@@ -315,10 +323,9 @@ while (<MOFILE>) {
 #      print TRFILE "\n\n\\section{$chap}\n";
     }
 
-    print HELPFILE "\n";
-    print HELPFILE "static const char *${mname}Help[] = {\n";
-#    print HELPFILE "static const std::vector<std::string> ${mname}Help = {\n";
-#    print HELPFILE "static const std::string ${mname}Help[] = {\n";
+    print HELPHH "extern const CdoHelp ${mname}Help;\n";
+    print HELPCC "\n";
+    print HELPCC "const CdoHelp ${mname}Help = {\n";
 
     @hkeys = split(" ", $moperators);
 #    print "$#hkeys @hkeys \n";
@@ -382,8 +389,8 @@ while (<MOFILE>) {
       print TRCARD "\\begin{tabular*}{$len1}{|>{\\columncolor{pcolor2}}l>{\\columncolor{pcolor1}}l|} \\hline\n";
     }
 
-    print HELPFILE "    \"NAME\",\n";
-    print HELPFILE "    \"    ";
+    print HELPCC "    \"NAME\",\n";
+    print HELPCC "    \"    ";
     $maxlen = 0;
     $maxitem = "";
     foreach $operator (@hkeys) {
@@ -401,7 +408,7 @@ while (<MOFILE>) {
     $slen = 0;
     foreach $operator (@hkeys) {
       if ( $icount > 0 ) {
-	print HELPFILE ", ";
+	print HELPCC ", ";
         $slen += 2;
       }
       $norefoper = 0;
@@ -413,24 +420,24 @@ while (<MOFILE>) {
       $len = length($operator);
       if ( $len + $slen > 76 ) {
 	$slen = 0;
-	print HELPFILE "\",\n";
-	print HELPFILE "    \"    ";
+	print HELPCC "\",\n";
+	print HELPCC "    \"    ";
       }
       $slen += $len;
-      print HELPFILE "${operator}";
+      print HELPCC "${operator}";
       if ( $norefoper == 1 ) {
 	$operator =~ s/^/-/;
       }
       $icount++;
     }
-    print HELPFILE " - ";
+    print HELPCC " - ";
     $len = length(${mtitle});
     if ( $len + $slen > 76 ) {
       $slen = 0;
-      print HELPFILE "\",\n";
-      print HELPFILE "    \"    ";
+      print HELPCC "\",\n";
+      print HELPCC "    \"    ";
     }
-    print HELPFILE "${mtitle}\",\n";
+    print HELPCC "${mtitle}\",\n";
 
     undef @oper_title;
     undef @oper_parameter;
@@ -530,13 +537,13 @@ while (<MOFILE>) {
       if ( $istart == 1 ) { $istart = 0; }
     }
 
-    print HELPFILE "    \"\",\n";
-    print HELPFILE "    \"SYNOPSIS\",\n";
+    print HELPCC "    \"\",\n";
+    print HELPCC "    \"SYNOPSIS\",\n";
     print TRFILE "\n\\subsection*{Synopsis}\n\n";
 
     if ( $istart > 0 )  {
       $parameter = @oper_parameter[0];
-      print HELPFILE "    \"    <operator>${parameter}  ${marguments}\",\n";
+      print HELPCC "    \"    <operator>${parameter}  ${marguments}\",\n";
       $parameter =~ s/_/\\_/g;
       print TRFILE "\\hspace*{8mm}{\$<\\!operator\\!>\$}\\textsl{$parameter} \\ \\texttt{$marguments}\n\n";
     }
@@ -546,7 +553,7 @@ while (<MOFILE>) {
         {
           $operator = @hkeys[$i];
           $parameter = @oper_parameter[$i];
-          printf HELPFILE ("    \"    %s%s  %s\",\n", $operator, $parameter, $marguments);
+          printf HELPCC ("    \"    %s%s  %s\",\n", $operator, $parameter, $marguments);
           $operatorx = $operator;
           $operatorx =~ s/_/\\_/g;
           $parameter =~ s/_/\\_/g;
@@ -554,8 +561,8 @@ while (<MOFILE>) {
        }
     }
 
-    print HELPFILE "    \"\",\n";
-    print HELPFILE "    \"DESCRIPTION\",\n";
+    print HELPCC "    \"\",\n";
+    print HELPCC "    \"DESCRIPTION\",\n";
 
     print TRFILE "\n\\subsection*{Description}\n\n";
     if ( $#moddeslines >= 0 ) {
@@ -580,8 +587,8 @@ while (<MOFILE>) {
     }
 
     if ( $#hkeys > 0 ) {
-      print HELPFILE "    \"\",\n";
-      print HELPFILE "    \"OPERATORS\",\n";
+      print HELPCC "    \"\",\n";
+      print HELPCC "    \"OPERATORS\",\n";
       print TRFILE "\n\\subsection*{Operators}\n\n";
     }
     else {
@@ -642,7 +649,7 @@ while (<MOFILE>) {
       $operatorx = $operator;
       $operatorx =~ s/_/\\_/g;
       if ( $#hkeys > 0 ) {
-	printf HELPFILE ("    \"    %-*s%s\",\n", $maxlen, $operator, $otitle);
+	printf HELPCC ("    \"    %-*s%s\",\n", $maxlen, $operator, $otitle);
 	if ( $xopercnt == 1 ) {
 	  print TRFILE "\\begin{defalist2}{\\textbf{$maxitem \\ }}\n";
 	}
@@ -769,8 +776,8 @@ while (<MOFILE>) {
     }
     
     if ( $lblock == 1 ) {
-      print HELPFILE "    \"\",\n";
-      print HELPFILE "    \"NAMELIST\",\n";
+      print HELPCC "    \"\",\n";
+      print HELPCC "    \"NAMELIST\",\n";
       
       print TRFILE "\n\\subsection*{Namelist}\n\n";
       begin_minipage();
@@ -818,8 +825,8 @@ while (<MOFILE>) {
 	}
       }
 
-      print HELPFILE "    \"\",\n";
-      print HELPFILE "    \"PARAMETER\",\n";
+      print HELPCC "    \"\",\n";
+      print HELPCC "    \"PARAMETER\",\n";
 
       print TRFILE "\n\\subsection*{Parameter}\n\n";
       begin_minipage();
@@ -833,7 +840,7 @@ while (<MOFILE>) {
 #	if ( index("$kword", "\@ITEM") != -1 ) {
 	if ( "$kword" eq "\@ITEM" ) {
 	  ($kopt, $value) = split(/\s*=\s*/, $line, 2);
-	  printf HELPFILE ("    \"    %-*s  ", $maxlen, $value);
+	  printf HELPCC ("    \"    %-*s  ", $maxlen, $value);
 	  $value =~ s/_/\\_/g;
 	  $value =~ s/</\$<\$/g;
 	  $value =~ s/>/\$>\$/g;
@@ -846,9 +853,9 @@ while (<MOFILE>) {
 	  $hline =~ s/\@celsius/°C/og;
 	  $hline =~ s/\@href\{([^}]*)\}\{([^}]*)\}/\2/g;
 	  if ( $lines > 1 )  {
-	    printf HELPFILE ("    \"    %-*s  ", $maxlen, " ");
+	    printf HELPCC ("    \"    %-*s  ", $maxlen, " ");
 	  }
-	  print HELPFILE "$hline\",\n";
+	  print HELPCC "$hline\",\n";
 	  $line =~ s/^FLOAT/\\makebox[20mm][l]{\\textsf{\\small FLOAT}}/;
 	  $line =~ s/^INTEGER/\\makebox[20mm][l]{\\textsf{\\small INTEGER}}/;
 	  $line =~ s/^STRING/\\makebox[20mm][l]{\\textsf{\\small STRING}}/;
@@ -904,8 +911,8 @@ while (<MOFILE>) {
 	}
       }
 
-      print HELPFILE "    \"\",\n";
-      print HELPFILE "    \"ENVIRONMENT\",\n";
+      print HELPCC "    \"\",\n";
+      print HELPCC "    \"ENVIRONMENT\",\n";
 
       print TRFILE "\n\\subsection*{Environment}\n\n";
       begin_minipage();
@@ -918,7 +925,7 @@ while (<MOFILE>) {
 #	if ( index("$kword", "\@ITEM") != -1 ) {
 	if ( "$kword" eq "\@ITEM" ) {
 	  ($kopt, $value) = split(/\s*=\s*/, $line, 2);
-	  printf HELPFILE ("    \"    %-*s\",\n", $maxlen, $value);
+	  printf HELPCC ("    \"    %-*s\",\n", $maxlen, $value);
 	  $value =~ s/_/\\_/g;
 	  print TRFILE "\\item[\\texttt{$value}\\ \\ \\hfill]\n";
 	} else {
@@ -927,10 +934,10 @@ while (<MOFILE>) {
 	  $hline =~ s/\\cite//og;
 	  $hline =~ s/\@env\{([^}]*)\}/\1/og;
 #	  if ( $lines == 0 ) {
-#	    print HELPFILE "$hline\",\n";
+#	    print HELPCC "$hline\",\n";
 #	  }
 #	  else {
-	    print HELPFILE "    \"        $hline\",\n";
+	    print HELPCC "    \"        $hline\",\n";
 #	  }
 #	    $line = /\sFLOAT/XXX
 	  $line =~ s/^FLOAT/\\makebox[20mm][l]\\textsf{\\small FLOAT}/;
@@ -968,8 +975,8 @@ while (<MOFILE>) {
     }
 
     if ( $lblock == 1 ) {
-      print HELPFILE "    \"\",\n";
-      print HELPFILE "    \"NOTE\",\n";
+      print HELPCC "    \"\",\n";
+      print HELPCC "    \"NOTE\",\n";
 
       print TRFILE "\n\\subsection*{Note}\n\n";
       begin_minipage();
@@ -1022,34 +1029,39 @@ while (<MOFILE>) {
 #      if ( $oper ne $operator ) {
 #	$nop = $nop + 1;
 #	if ( $nop == 1 ) {
-#	  print HELPFILE "    \"\",\n";
-#	  print HELPFILE "    \"SEE ALSO\",\n";
+#	  print HELPCC "    \"\",\n";
+#	  print HELPCC "    \"SEE ALSO\",\n";
 #	}
 #	if ( $linelen > 70 ) {
 #	  $linelen = 0;
-#	  print HELPFILE ",\",\n";
+#	  print HELPCC ",\",\n";
 #	}
 #	if ( $linelen == 0 ) {
-#	  print HELPFILE "    \"    $oper";
+#	  print HELPCC "    \"    $oper";
 #	}
 #	else {
-#	  print HELPFILE ", $oper";
+#	  print HELPCC ", $oper";
 #	}
 #	$linelen = $linelen + length($oper) + 2;
 #      }
 #    }
 #    if ( $lineend == 0 && $nop > 0 ) {
-#      print HELPFILE "\",\n";
+#      print HELPCC "\",\n";
 #    }
 
 #   End
 
-#      print HELPFILE "    \"\",\n";
-#      print HELPFILE "    \"\@End_${operator}\",\n";
+#      print HELPCC "    \"\",\n";
+#      print HELPCC "    \"\@End_${operator}\",\n";
+
+#    print HELPCC "    \"ENDHELP\"\n";
+    print HELPCC "};\n";
 
-#    print HELPFILE "    \"ENDHELP\"\n";
-    print HELPFILE "    nullptr\n";
-    print HELPFILE "};\n";
+#    print HELPCC "extern char const* const* ${mname}Help;\n";
+
+#    print HELPCC "\n";
+#    print HELPCC "char const* const* ${mname}Help_get() { return ${mname}Help; }\n";
+#    print HELPCC "\n";
 
     print TCFILE "\\end{tabular*}\n";
     print TCFILE "\\vspace{1mm}\n";
@@ -1093,11 +1105,14 @@ foreach my $line (sort(@oplist)){
 print ABCLIST "\\end{longtable}\n";
 print ABCLIST "\n";
 
-print HELPFILE "// clang-format on\n";
-print HELPFILE "\n";
-print HELPFILE "#endif\n";
+print HELPHH "\n";
+print HELPHH "#endif\n";
+
+print HELPCC "\n";
+print HELPCC "// clang-format on\n";
+print HELPCC "\n";
 
-close(HELPFILE);
+close(HELPCC);
 close(TRFILE);
 close(TCFILE);
 close(ABCLIST);
@@ -1170,7 +1185,7 @@ sub print_description {
       $value =~ s/</\$<\$/og;
       $value =~ s/>/\$>\$/og;
       if ( $help_print == 1 ) {
-	printf HELPFILE ("    \"    %*s%s- ${hvalue}\",\n", $nspace, "");
+	printf HELPCC ("    \"    %*s%s- ${hvalue}\",\n", $nspace, "");
       }
       $value =~ s/_/\\_/g;
       print TRFILE "\\item $value\n";
@@ -1179,9 +1194,9 @@ sub print_description {
       if ( $help_print == 1 ) {
 	$ntab = $noff - length($value);
 	if ( $ntab < 0 ) { $ntab = 0 };
-#	printf HELPFILE ("    \"    %*s%s%s%*s%s\"", $nspace, "", ${value}, $ntab, "");
-	printf HELPFILE ("    \"    %*s%s${value}", $nspace, "");
-	printf HELPFILE ("%*s%s\"", $ntab, "");
+#	printf HELPCC ("    \"    %*s%s%s%*s%s\"", $nspace, "", ${value}, $ntab, "");
+	printf HELPCC ("    \"    %*s%s${value}", $nspace, "");
+	printf HELPCC ("%*s%s\"", $ntab, "");
       }
       print TRFILE "\\item[{$value}\\ \\ \\hfill]\n";
     } elsif ( "$kword" eq "\@BEGINMATH" ) {
@@ -1296,7 +1311,7 @@ sub print_description {
 	$ospace = $nspace;
 	if ( $list == 1 ) {$ospace=0;}
 	if ( $help_print == 1 ) {
-	  printf HELPFILE ("    \"    %*s%s\",\n", $ospace, "", $docline);
+	  printf HELPCC ("    \"    %*s%s\",\n", $ospace, "", $docline);
 	}
       }
     }
diff --git a/doc/tex/mod/Derivepar b/doc/tex/mod/Derivepar
index 058c6fd0ea724c3fc43bd82168b24bd63b69bf89..b2fe80311174371aa1cdc08655470b0dd29c1f46 100644
--- a/doc/tex/mod/Derivepar
+++ b/doc/tex/mod/Derivepar
@@ -1,13 +1,14 @@
 @BeginModule
+@NewPage
 @Name      = Derivepar
 @Title     = Derived model parameters
 @Section   = Miscellaneous
 @Arguments = infile outfile
-@Operators = sealevelpressure gheight
+@Operators = sealevelpressure gheight gheight_half
 
 @BeginDescription
 This module contains operators that calculate derived model parameters. These are currently the parameters
-sea level pressure and geopotential height. All necessary input parameters are identified by their GRIB1
+sea level pressure and geopotential height. All necessary input variables are identified by their GRIB1
 code number or the NetCDF CF standard name.
 Supported GRIB1 parameter tables are: WMO standard table number 2 and ECMWF local table number 128.
 
@@ -35,10 +36,22 @@ are surface_air_pressure, surface_geopotential and air_temperature on full hybri
 
 
 @BeginOperator_gheight
-@Title     = Geopotential height
+@Title     = Geopotential height on full-levels
 
 @BeginDescription
-This operator computes the geopotential height (geopotential_height) on full model levels in metres.
+This operator computes the geopotential height (geopotential_height) on model full-levels in metres.
+Required input fields are surface_air_pressure, surface_geopotential, specific_humidity and air_temperature
+on full hybrid sigma pressure levels. Note, this procedure is an approximation, which doesn't take into
+account the effects of e.g. cloud ice and water, rain and snow.
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_gheight_half
+@Title     = Geopotential height on half-levels
+
+@BeginDescription
+This operator computes the geopotential height (geopotential_height) on model half-levels in metres.
 Required input fields are surface_air_pressure, surface_geopotential, specific_humidity and air_temperature
 on full hybrid sigma pressure levels. Note, this procedure is an approximation, which doesn't take into
 account the effects of e.g. cloud ice and water, rain and snow.
diff --git a/doc/tex/mod/EcaCwd b/doc/tex/mod/EcaCwd
index f7b037a74885e3ac545fd3c5854a8218db11eda6..c6d3670ce2b92950e70a1dde9788fb7c8f90f926 100644
--- a/doc/tex/mod/EcaCwd
+++ b/doc/tex/mod/EcaCwd
@@ -17,7 +17,8 @@ Parameter is a comma-separated list of "key=values" pairs.
 
 @BeginOperator_eca_cwd
 @Title     = Consecutive wet days index per time period
-@Parameter = [R] [N] [params]
+@Parameter = [params]
+
 @BeginDescription
 The operator counts over the entire time series.
 The date information of a timestep in @file{outfile} is the date of
@@ -25,9 +26,10 @@ the last contributing timestep in @file{infile}.
 @EndDescription
 @EndOperator
 
-@BeginOperator_eca_cwd
+@BeginOperator_etccdi_cwd
 @Title     = Consecutive wet days index per time period
-@Parameter = [R] [N] [params]
+@Parameter =[params]
+
 @BeginDescription
 The default output frequency is yearly.
 Periods within overlapping years are accounted for the first year.
diff --git a/doc/tex/mod/EcaHwfi b/doc/tex/mod/EcaHwfi
index 3b889af46d7ab5956a6a7aedc5f73d470bf2e3be..b0be5868282ece170da7ba2676bac9dc861ab218 100644
--- a/doc/tex/mod/EcaHwfi
+++ b/doc/tex/mod/EcaHwfi
@@ -25,7 +25,8 @@ Parameter is a comma-separated list of "key=values" pairs.
 
 @BeginOperator_eca_hwfi
 @Title     = Warm spell days index wrt 90th percentile of reference period
-@Parameter = [nday] [params]
+@Parameter = [params]
+
 @BeginDescription
 The operator counts over the entire time series.
 The date information of a timestep in @file{outfile} is the date of
@@ -33,9 +34,10 @@ the last contributing timestep in @file{infile}.
 @EndDescription
 @EndOperator
 
-BeginOperator_etccdi_wsdi
+@BeginOperator_etccdi_wsdi
 @Title     = Warm Spell Duration Index
-@Parameter = [nday] [params]
+@Parameter = [params]
+
 @BeginDescription
 The default output frequency is yearly.
 Periods within overlapping years are accounted for the first year.
diff --git a/doc/tex/mod/Exprf b/doc/tex/mod/Exprf
index 65132c8eb711d05499e27c2693a16866e76a1b0c..5dffe41d6c2afdb97dbcfc8f4fd143cef13f1541 100644
--- a/doc/tex/mod/Exprf
+++ b/doc/tex/mod/Exprf
@@ -124,7 +124,7 @@ Arc tangent function of y/x, using signs to determine quadrants
 
 Coordinates:
 
-@BeginList gridarea(x)
+@BeginList cthickness(x)
 @Item = clon(x)
 Longitude coordinate of x (available only if x has geographical coordinates)
 @Item = clat(x)
diff --git a/doc/tex/mod/Gridcell b/doc/tex/mod/Gridcell
index a1c953a7fd7fe97cb74e18c9532c71fc4e0a2eb0..5ed86efc7cf6efcebe9ca08e747e43464b5ddba0 100644
--- a/doc/tex/mod/Gridcell
+++ b/doc/tex/mod/Gridcell
@@ -8,8 +8,8 @@
 @BeginDescription
 This module reads the grid cell area of the first grid from the input stream. If the grid cell area is missing it
 will be computed from the grid coordinates. The area of a grid cell is calculated using spherical triangles from
-the coordinates of the center and the vertices. The base is a unit sphere which is scaled with the radius of the earth.
-The default earth radius is 6371000 meter. This value can be changed with the environment variable PLANET_RADIUS.
+the coordinates of the center and the vertices. The base is a unit sphere which is scaled with the radius of the planet.
+The default planet radius is 6371000 meter. The parameter @var{radius} or the environment variable @env{PLANET_RADIUS} can be used to change the default.
 Depending on the chosen operator the grid cell area or weights are written to the output stream.
 @EndDescription
 @EndModule
@@ -17,10 +17,11 @@ Depending on the chosen operator the grid cell area or weights are written to th
 
 @BeginOperator_gridarea
 @Title     = Grid cell area
+@Parameter = [radius]
 
 @BeginDescription
 Writes the grid cell area to the output stream. If the grid cell area have to
-be computed it is scaled with the earth radius to square meters.
+be computed it is scaled with the planet radius to square meters.
 @EndDescription
 @EndOperator
 
@@ -32,6 +33,10 @@ Writes the grid cell area weights to the output stream.
 @EndDescription
 @EndOperator
 
+@BeginParameter maxpoints
+@Item = radius
+FLOAT   Planet radius in meter
+@EndParameter
 
 @BeginEnvironment
 @Item = PLANET_RADIUS
diff --git a/doc/tex/mod/Intlevel b/doc/tex/mod/Intlevel
index a7e45bb07ad53dff1cdb91787a3a4245a11404d3..643a75c856fcf033ede96d83ba72a1ec65f0d890 100644
--- a/doc/tex/mod/Intlevel
+++ b/doc/tex/mod/Intlevel
@@ -8,7 +8,7 @@
 @Operators = intlevel
 
 @BeginDescription
-This operator performs a linear vertical interpolation of 3D variables. The target levels can be
+This operator performs a linear vertical interpolation of 3D variables. The 1D target levels can be
 specified with the level parameter or read in via a Z-axis description file.
 @EndDescription
 @EndModule
@@ -23,8 +23,10 @@ specified with the level parameter or read in via a Z-axis description file.
 @BeginParameter
 @Item = level
 FLOAT   Comma-separated list of target levels
-@Item = file
+@Item = zaxisdescription
 STRING  Path to a file containing a description of the Z-axis
+@Item = zvarname
+STRING  Use zvarname as the vertical 3D source coordinate instead of the 1D coordinate variable
 @EndParameter
 
 
diff --git a/doc/tex/mod/Pack b/doc/tex/mod/Pack
index 6e93c4659225defd6b1e4093fc339fcd38017d14..05b27926ca7e2f9a25e307dd5c66503621de66dd 100644
--- a/doc/tex/mod/Pack
+++ b/doc/tex/mod/Pack
@@ -13,6 +13,7 @@
 
 @BeginOperator_pack
 @Title     = Pack data
+@Parameter = [parameter]
 
 @BeginDescription
 Packing reduces the data volume by reducing the precision of the stored numbers.
@@ -21,5 +22,14 @@ The operator @oper{pack} calculates the attributes @env{add_offset} and @env{sca
 The default data type for all variables is automatically changed to 16-bit integer.
 Use the CDO option -b to change the data type to a different integer precision, if needed.
 Missing values are automatically transformed to the current data type.
+
+Alternatively, the pack parameters @env{add_offset} and @env{scale_factor} can be read from a file for each variable.
 @EndDescription
 @EndOperator
+
+@BeginParameter filename
+@Item = printparam
+BOOL    Print pack parameters to stdout for each variable
+@Item = filename
+STRING  Read pack parameters from file for each variable[format: name=<> add_offset=<> scale_factor=<>]
+@EndParameter
diff --git a/doc/tex/mod/Pressure b/doc/tex/mod/Pressure
new file mode 100644
index 0000000000000000000000000000000000000000..b712632879de54f64da4c19392ddcfcc853a592c
--- /dev/null
+++ b/doc/tex/mod/Pressure
@@ -0,0 +1,86 @@
+@BeginModule
+@NewPage
+@Name      = Pressure
+@Title     = Pressure on model levels
+@Section   = Miscellaneous
+@Arguments = infile outfile
+@Operators = pressure_half pressure delta_pressure
+
+@BeginDescription
+This module contains operators to calculate the pressure on model levels.
+To calculate the pressure on model levels, the a and b coefficients defining the model levels and
+the surface pressure are required. The a and b coefficients are normally part of the model level data.
+If not available, the surface pressure can be derived from the logarithm of the surface pressure.
+The surface pressure is identified by the GRIB1 code number or NetCDF CF standard name.
+
+@BeginTable4
+ @bold{Name}                       & @bold{Units}      & @bold{GRIB1 code} & @bold{CF standard name}
+   log surface pressure     &  Pa        &  152       &
+   surface pressure         &  Pa        &  134       &  surface_air_pressure
+@EndTable
+
+@EndDescription
+@EndModule
+
+
+@BeginOperator_pressure_half
+@Title     = Pressure on half-levels
+
+@BeginDescription
+This operator computes the pressure on model half-levels in pascal.
+The model half-level pressure (p_half) is given by:
+
+@IfMan
+
+        p_half = a + b ∗ sp
+@EndifMan
+@IfDoc
+\begin{displaymath}
+        p_half = a + b \ast sp
+\end{displaymath}
+@EndifDoc
+
+with
+   a, b: coefficients defining the model levels
+   sp: surface pressure
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_pressure
+@Title     = Pressure on full-levels
+
+@BeginDescription
+This operator computes the pressure on model full-levels in pascal.
+The pressure on model full-levels (p_full) is in the middle of the layers defined by the model half-levels:
+
+@IfMan
+        p_full = (p_half_above + p_half_below) / 2
+@EndifMan
+@IfDoc
+\begin{displaymath}
+        p_full = \frac{p_half_above + p_half_below}{2}
+\end{displaymath}
+@EndifDoc
+
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_delta_pressure
+@Title     = Pressure difference of half-levels
+
+@BeginDescription
+This operator computes the pressure difference between to model half-levels.
+
+@IfMan
+        delta_p = p_half_below - p_half_above
+@EndifMan
+@IfDoc
+\begin{displaymath}
+        delta_p = p_half_below - p_half_above
+\end{displaymath}
+@EndifDoc
+
+@EndDescription
+@EndOperator
diff --git a/doc/tex/mod/Vertint b/doc/tex/mod/Vertint
index a834a9d4b130472a4b8435d2012f1f6a76b7ef9b..611cdb8e7563ac775172db6ed60cd4b9adaf7b7f 100644
--- a/doc/tex/mod/Vertint
+++ b/doc/tex/mod/Vertint
@@ -9,20 +9,21 @@
 
 @BeginDescription
 Interpolates 3D variables on hybrid sigma pressure level to pressure or height levels.
-The input file should contain the log. surface pressure or the surface pressure.
+To calculate the pressure on model levels, the a and b coefficients defining the model levels and
+the surface pressure are required. The a and b coefficients are normally part of the model level data.
+If not available, the surface pressure can be derived from the logarithm of the surface pressure.
 To extrapolate the temperature, the surface geopotential is also needed.
-It is assumed that the geopotential heights are located at the hybrid layer interfaces.
-For the lowest layer of geopotential heights the surface geopotential is required.
-The pressure, temperature, geopotential height, and surface geopotential are identified by
-their GRIB1 code number or NetCDF CF standard name.
+The geopotential height must be present at the hybrid layer interfaces (model half-layers)!
+All needed variables are identified by their GRIB1 code number or NetCDF CF standard name.
 Supported parameter tables are: WMO standard table number 2 and ECMWF local table number 128.
 
-@BeginTable
- @bold{CF standard name}            & @bold{Units}      & @bold{GRIB 1 code}      
-   surface_air_pressure      &  Pa        &  134
-   air_temperature           &  K         &  130
-   surface_geopotential      &  m2 s-2    &  129
-   geopotential_height       &  m         &  156
+@BeginTable4
+ @bold{Name}                       & @bold{Units}      & @bold{GRIB1 code} & @bold{CF standard name}
+   log surface pressure     &  Pa        &  152       &
+   surface pressure         &  Pa        &  134       &  surface_air_pressure
+   air temperature          &  K         &  130       &  air_temperature
+   surface geopotential     &  m2 s-2    &  129       &  surface_geopotential
+   geopotential height      &  m         &  156       &  geopotential_height
 @EndTable
 
 Use the alias  @bold{ml2plx}/@bold{ml2hlx} or the environment variable @env{EXTRAPOLATE} to extrapolate
diff --git a/doc/tex/mod/Wind b/doc/tex/mod/Wind
index d8e693201e684c0146162fe45b0e69d02f91642b..28440bd93eb6be0c83dd537dd53c0bfb99df5722 100644
--- a/doc/tex/mod/Wind
+++ b/doc/tex/mod/Wind
@@ -72,8 +72,7 @@ names u and v or the code numbers 131 and 132.
 
 @BeginParameter gridtype
 @Item = gridtype
-STRING  Type of the grid: quadratic, linear (default: quadratic)
-@C STRING  Type of the grid: quadratic, linear, cubic (default: quadratic)
+STRING  Type of the grid: quadratic, linear, cubic (default: quadratic)
 @EndParameter
 
 
diff --git a/libcdi b/libcdi
index 395ab6995d195c9bd5f7f7d33d08b1da8f926806..d64b85168fad6fd7a8bebe3a12e386b0f8c6a41a 160000
--- a/libcdi
+++ b/libcdi
@@ -1 +1 @@
-Subproject commit 395ab6995d195c9bd5f7f7d33d08b1da8f926806
+Subproject commit d64b85168fad6fd7a8bebe3a12e386b0f8c6a41a
diff --git a/scripts/cppcheck.sh b/scripts/cppcheck.sh
index a5bb31025c17a90798785f93244c1b34da539ffb..6d3b1eef3632240e3023fca27c56a8014daa025e 100755
--- a/scripts/cppcheck.sh
+++ b/scripts/cppcheck.sh
@@ -22,7 +22,7 @@ echo $TOPDIR
 # set -x
 echo "" > ${LOG_FILE}
 dirname=$TOPDIR/src
-cppcheck --std=c++17  --force --enable=all --inline-suppr --template='{file}:{line},{severity},{id},{message}' \
+cppcheck --std=c++20  --force --enable=all --inline-suppr --template='{file}:{line},{severity},{id},{message}' \
          -I${TOPDIR}/build/gnu/src -I${TOPDIR}/libcdi/src \
          -DHAVE_CONFIG_H  \
          "$dirname" >>${LOG_FILE} 2>&1
diff --git a/src/Adisit.cc b/src/Adisit.cc
index 4f527786309f3210d153f94dd06cb835f08f8af4..a772ac61f8cd4796a29ce2a9e6d4e5b7dd4c5cd3 100644
--- a/src/Adisit.cc
+++ b/src/Adisit.cc
@@ -133,8 +133,8 @@ get_code(int vlistID1, int varID, const std::string &cname)
       auto varname = string_to_lower(cdo::inq_var_name(vlistID1, varID));
       auto stdname = string_to_lower(cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME));
 
-      if (varname == "s" || stdname == "sea_water_salinity") { code = 5; }
-      else if (varname == "t") { code = 2; }
+      if (varname == "s" || varname == "so" || stdname == "sea_water_salinity") { code = 5; }
+      else if (varname == "t" || varname == "to") { code = 2; }
 
       if (stdname == cname) code = 2;
     }
@@ -236,8 +236,20 @@ configureOutput(const std::function<void(const int, const int)> &outputSettingFu
   return IOSettings{ streamID2, vlistID2, gridsize, nlevels, taxisID1, taxisID2, tisID2, saoID2 };
 }
 
-class ModuleAdisit
+class Adisit : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Adisit",
+    .operators = { { "adisit", 0, 0, "", AdisitHelp }, { "adipot", 0, 0, "", AdisitHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Adisit> registration = RegisterEntry<Adisit>(module);
+  int ADISIT, ADIPOT;
   int thoID = -1, saoID = -1;
 
   CdoStreamID streamID1;
@@ -258,16 +270,13 @@ class ModuleAdisit
   FieldVector tho, sao, tis;
   Varray<double> pressure;
 
-  int ADISIT;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    ADISIT = cdo_operator_add("adisit", 0, 0, "");
-    auto ADIPOT = cdo_operator_add("adipot", 0, 0, "");
+    ADISIT = module.get_id("adisit");
+    ADIPOT = module.get_id("adipot");
 
     operatorID = cdo_operator_id();
 
@@ -278,7 +287,7 @@ public:
 
     std::string cname_ADISIT{ "sea_water_potential_temperature" };
     std::string cname_ADIPOT{ "sea_water_temperature" };
-    std::string cname = std::string{ (operatorID == ADISIT) ? cname_ADISIT : cname_ADIPOT };
+    std::string cname = (operatorID == ADISIT) ? cname_ADISIT : cname_ADIPOT;
 
     for (int varID = 0; varID < nvars; ++varID)
       {
@@ -335,8 +344,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            if (varID == thoID) cdo_read_record(streamID1, tho[levelID].vec_d.data(), &tho[levelID].nmiss);
-            if (varID == saoID) cdo_read_record(streamID1, sao[levelID].vec_d.data(), &sao[levelID].nmiss);
+            if (varID == thoID) cdo_read_record(streamID1, tho[levelID].vec_d.data(), &tho[levelID].numMissVals);
+            if (varID == saoID) cdo_read_record(streamID1, sao[levelID].vec_d.data(), &sao[levelID].numMissVals);
 
             if (varID == thoID)
               {
@@ -376,19 +385,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Adisit(void *process)
-{
-  ModuleAdisit adisit;
-
-  adisit.init(process);
-  adisit.run();
-  adisit.close();
-
-  return nullptr;
-}
diff --git a/src/Afterburner.cc b/src/Afterburner.cc
index 1ed82902d2f9e8bb8188af732addfa0ac7369d7e..29c38dfd5f5ebc58c3132dec590d2ace0a1acc02 100644
--- a/src/Afterburner.cc
+++ b/src/Afterburner.cc
@@ -62,7 +62,7 @@ static int ofiletype = -1;
 static int DataType = -1;
 
 static char *filename;
-static const char **ifiles;
+static std::vector<const char *> ifiles;
 static char *ifile = nullptr;
 
 static int ofileidx = 0;
@@ -71,6 +71,7 @@ static int specGridID = -1;
 static int gaussGridID = -1;
 static int iVertID = -1;
 static int oVertID = -1;
+static int oVertID_half = -1;
 
 static bool Lhybrid2pressure = false;
 
@@ -112,7 +113,7 @@ FreeMean(struct Variable *vars)
   for (int code = 0; code < MaxCodes; ++code)
     if (vars[code].mean)
       {
-        Free(vars[code].mean);
+        free(vars[code].mean);
         vars[code].mean = nullptr;
       }
 }
@@ -282,7 +283,7 @@ after_SwitchFile(AfterControl *globs)
 }
 
 static CdiDateTime
-after_getDateTime(struct Date datetime)
+after_getDateTime(const struct Date &datetime)
 {
   CdiDateTime cdiDateTime{};
   cdiDateTime.date = cdiDate_encode(datetime.yr, datetime.mo, datetime.dy);
@@ -376,8 +377,8 @@ static int num_recs = 0;
 static void *
 after_readTimestep(void *arg)
 {
-  int varID, gridID, zaxisID, levelID, timeID;
-  size_t nmiss;
+  int varID, gridID, zaxisID, levelID, timeType;
+  size_t numMissVals;
   auto rarg = (RARG *) arg;
 
   auto nrecs = rarg->nrecs;
@@ -385,7 +386,7 @@ after_readTimestep(void *arg)
   auto vars = rarg->vars;
   auto globs = rarg->globs;
 
-  for (int code = 0; code < MaxCodes; ++code) vars[code].nmiss0 = 0;
+  for (int code = 0; code < MaxCodes; ++code) vars[code].numMissVals0 = 0;
 
   for (int recID = 0; recID < nrecs; ++recID)
     {
@@ -397,7 +398,7 @@ after_readTimestep(void *arg)
       // Skip records containing unneeded codes
       if (!vars[code].needed0) continue;
 
-      vlistInqVar(globs->ivlistID, varID, &gridID, &zaxisID, &timeID);
+      vlistInqVar(globs->ivlistID, varID, &gridID, &zaxisID, &timeType);
 
       auto leveltype = zaxisInqType(zaxisID);
 
@@ -433,14 +434,14 @@ after_readTimestep(void *arg)
 
       if (analysisData)
         {
-          streamReadRecord(globs->istreamID, globs->Field, &nmiss);
-          after_AnalysisAddRecord(globs, vars, code, gridID, zaxisID, levelID, nmiss);
+          streamReadRecord(globs->istreamID, globs->varray.data(), &numMissVals);
+          after_AnalysisAddRecord(globs, vars, code, gridID, zaxisID, levelID, numMissVals);
         }
       else
         {
           double *dataptr = after_get_dataptr(vars, code, gridID, zaxisID, levelID);
-          streamReadRecord(globs->istreamID, dataptr, &nmiss);
-          after_EchamAddRecord(globs, vars, code, gridID, zaxisID, levelID, nmiss);
+          streamReadRecord(globs->istreamID, dataptr, &numMissVals);
+          after_EchamAddRecord(globs, vars, code, gridID, zaxisID, levelID, numMissVals);
         }
 
       if (iVertID != -1 && oVertID != -1 && (vars[code].izaxisID == iVertID)) vars[code].ozaxisID = oVertID;
@@ -499,7 +500,7 @@ after_setEndOfInterval(AfterControl &globs, int nrecs)
 static void
 after_moveTimestep(struct Variable *vars)
 {
-  for (int code = 0; code < MaxCodes; ++code) vars[code].nmiss = vars[code].nmiss0;
+  for (int code = 0; code < MaxCodes; ++code) vars[code].numMissVals = vars[code].numMissVals0;
 
   for (int code = 0; code < MaxCodes; ++code)
     if (vars[code].hybrid0)
@@ -697,8 +698,8 @@ after_setLevel(AfterControl &globs)
   int numplevelDefault = sizeof(plevelDefault) / sizeof(plevelDefault[0]);
   int numhlevelDefault = sizeof(hlevelDefault) / sizeof(hlevelDefault[0]);
 
-  if (iVertID != -1)
-    if (zaxisInqType(iVertID) == ZAXIS_HYBRID && globs.Type > 20) Lhybrid2pressure = true;
+  int iLevelType = (iVertID != -1) ? zaxisInqType(iVertID) : -1;
+  if (iLevelType == ZAXIS_HYBRID && globs.Type > 20) Lhybrid2pressure = true;
 
   if (globs.Verbose) lprintf(stdout);
 
@@ -733,13 +734,15 @@ after_setLevel(AfterControl &globs)
             {
               if (globs.Verbose)
                 {
-                  if (zaxisInqType(iVertID) == ZAXIS_HYBRID)
+                  if (iLevelType == ZAXIS_HYBRID)
                     fprintf(stdout, " All detected hybrid level selected:\n");
                   else
                     fprintf(stdout, " All detected pressure level selected:\n");
                 }
               globs.NumLevelRequest = globs.NumLevelFound;
               for (int l = 0; l < globs.NumLevelRequest; ++l) globs.LevelRequest[l] = LevelFound[l];
+              int maxLev = globs.NumLevelFound;
+              if (maxLev == (globs.numHalfLevels - 1)) globs.LevelRequest[maxLev] = globs.numHalfLevels;
               oVertID = iVertID;
             }
         }
@@ -762,22 +765,14 @@ after_setLevel(AfterControl &globs)
         {
           if (Lhybrid2pressure)
             {
-              if (globs.unitsel == 0)
-                fprintf(stdout, " Selected pressure level:\n");
-              else
-                fprintf(stdout, " Selected height level:\n");
+              fprintf(stdout, " Selected %s level:\n", (globs.unitsel == 0) ? "pressure" : "height");
             }
           else
             {
-              if (zaxisInqType(iVertID) == ZAXIS_HYBRID)
+              if (iLevelType == ZAXIS_HYBRID)
                 fprintf(stdout, " Selected hybrid level:\n");
               else
-                {
-                  if (globs.unitsel == 0)
-                    fprintf(stdout, " Selected pressure level:\n");
-                  else
-                    fprintf(stdout, " Selected height level:\n");
-                }
+                fprintf(stdout, " Selected %s level:\n", (globs.unitsel == 0) ? "pressure" : "height");
             }
         }
     }
@@ -857,11 +852,12 @@ after_defineLevel(const AfterControl &globs, struct Variable *vars)
       {
         if (iVertID == -1) break;
 
-        if (zaxisInqType(iVertID) == ZAXIS_HYBRID)
+        int iVertType = zaxisInqType(iVertID);
+        if (iVertType == ZAXIS_HYBRID || iVertType == ZAXIS_HYBRID_HALF)
           {
             if (oVertID == -1)
               {
-                if (globs.NumLevelRequest > globs.NumLevelFound) afterAbort("Too much level requested");
+                if (globs.NumLevelRequest > globs.NumLevelFound) afterAbort("Too many level requested");
 
                 if (globs.NumLevelFound == globs.NumLevelRequest)
                   {
@@ -876,7 +872,17 @@ after_defineLevel(const AfterControl &globs, struct Variable *vars)
                   {
                     oVertID = zaxisCreate(ZAXIS_HYBRID, globs.NumLevelRequest);
                     zaxisDefLevels(oVertID, globs.LevelRequest);
-                    zaxisDefVct(oVertID, globs.nvct, globs.vct);
+                    zaxisDefVct(oVertID, globs.nvct, globs.vct.data());
+                  }
+              }
+
+            if (vars[GEOPOTHEIGHT].selected && oVertID_half == -1)
+              {
+                if (oVertID_half == -1 && globs.NumLevelRequest > 0)
+                  {
+                    oVertID_half = zaxisCreate(ZAXIS_HYBRID_HALF, globs.NumLevelRequest + 1);
+                    zaxisDefLevels(oVertID_half, globs.LevelRequest);
+                    zaxisDefVct(oVertID_half, globs.nvct, globs.vct.data());
                   }
               }
 
@@ -884,10 +890,16 @@ after_defineLevel(const AfterControl &globs, struct Variable *vars)
               {
                 if (vars[code].selected)
                   {
-                    if (vars[code].izaxisID != -1)
-                      if (zaxisInqType(vars[code].izaxisID) == ZAXIS_HYBRID
-                          && zaxisInqSize(vars[code].izaxisID) >= globs.NumLevelRequest)
-                        vars[code].ozaxisID = oVertID;
+                    int zaxisID = vars[code].izaxisID;
+                    if (zaxisID != -1)
+                      {
+                        int zaxisType = zaxisInqType(zaxisID);
+                        int numLevels = zaxisInqSize(zaxisID);
+                        if ((zaxisType == ZAXIS_HYBRID || zaxisType == ZAXIS_HYBRID_HALF) && numLevels >= globs.NumLevelRequest)
+                          {
+                            vars[code].ozaxisID = (code == GEOPOTHEIGHT) ? oVertID_half : oVertID;
+                          }
+                      }
                   }
               }
           }
@@ -918,11 +930,12 @@ after_defineLevel(const AfterControl &globs, struct Variable *vars)
           {
             if (vars[code].selected)
               {
-                if (vars[code].izaxisID != -1)
+                int zaxisID = vars[code].izaxisID;
+                if (zaxisID != -1)
                   {
-                    int nlev = zaxisInqSize(vars[code].izaxisID);
-                    if (zaxisInqType(vars[code].izaxisID) == zaxisInqType(iVertID)
-                        && (nlev == globs.NumLevel || nlev == globs.NumLevel + 1) && nlev > 1)
+                    int zaxisType = zaxisInqType(zaxisID);
+                    int numLevels = zaxisInqSize(zaxisID);
+                    if (zaxisType == zaxisInqType(iVertID) && (numLevels == globs.NumLevel || numLevels == globs.NumLevel + 1) && numLevels > 1)
                       vars[code].ozaxisID = oVertID;
                   }
               }
@@ -1137,6 +1150,8 @@ after_parini(AfterControl &globs, struct Variable *vars)
     }
   namelist[i] = 0;
 
+  globs.Debug = scan_par(globs.Verbose, namelist, "debug", 0);
+
   if (globs.Debug)
     {
       lprintf(stderr);
@@ -1260,7 +1275,7 @@ after_precntl(AfterControl &globs, struct Variable *vars)
 {
   int vertfound = 0;
   int nhzaxis = 0;
-  int FieldDim = 0;
+  int gridSizeMax = 0;
 
   auto nvars = vlistNvars(globs.ivlistID);
   auto ngrids = vlistNgrids(globs.ivlistID);
@@ -1281,7 +1296,7 @@ after_precntl(AfterControl &globs, struct Variable *vars)
       auto gridtype = gridInqType(gridID);
       int datasize = gridInqSize(gridID);
 
-      if (datasize > FieldDim) FieldDim = datasize;
+      if (datasize > gridSizeMax) gridSizeMax = datasize;
 
       if (gridtype == GRID_SPECTRAL && globs.Truncation == 0)
         {
@@ -1314,6 +1329,7 @@ after_precntl(AfterControl &globs, struct Variable *vars)
             case 64: globs.Truncation = 42; break;
             case 48: globs.Truncation = 31; break;
             case 32: globs.Truncation = 21; break;
+            case 1: break;
             default: fprintf(stderr, "%d Gaussian latitudes not supported.\n", globs.Latitudes);
             }
         }
@@ -1324,18 +1340,15 @@ after_precntl(AfterControl &globs, struct Variable *vars)
       auto zaxisID = vlistZaxis(globs.ivlistID, index);
       auto leveltype = zaxisInqType(zaxisID);
       auto numlevel = zaxisInqSize(zaxisID);
-      /*
-        printf("leveltype : %d %d\n", leveltype, zaxisInqSize(zaxisID));
-      */
       if (numlevel > 1)
         {
-          if (leveltype == ZAXIS_HYBRID || leveltype == ZAXIS_PRESSURE)
+          if (leveltype == ZAXIS_HYBRID || leveltype == ZAXIS_HYBRID_HALF || leveltype == ZAXIS_PRESSURE)
             {
-              if (leveltype == ZAXIS_HYBRID && globs.nvct == 0)
+              if ((leveltype == ZAXIS_HYBRID || leveltype == ZAXIS_HYBRID_HALF) && globs.nvct == 0)
                 {
                   nhzaxis++;
                   int nvct = zaxisInqVctSize(zaxisID);
-                  if (numlevel != (nvct / 2 - 1))
+                  if (numlevel != (nvct / 2 - 1) && numlevel != (nvct / 2))
                     {
                       if (nvct == 0)
                         {
@@ -1345,8 +1358,10 @@ after_precntl(AfterControl &globs, struct Variable *vars)
                       continue;
                     }
                 }
-              else if (leveltype == ZAXIS_HYBRID && globs.nvct == zaxisInqVctSize(zaxisID))
-                continue;
+              else if ((leveltype == ZAXIS_HYBRID || leveltype == ZAXIS_HYBRID_HALF) && globs.nvct == zaxisInqVctSize(zaxisID))
+                {
+                  continue;
+                }
 
               if (iVertID != -1) cdo_warning("More than %d different vertical grid structure found!", vertfound);
 
@@ -1359,29 +1374,35 @@ after_precntl(AfterControl &globs, struct Variable *vars)
               LevelFound.resize(globs.NumLevelFound);
               for (int l = 0; l < globs.NumLevelFound; ++l) LevelFound[l] = (int) zaxisInqLevel(zaxisID, l);
 
-              if (leveltype == ZAXIS_HYBRID)
+              if (leveltype == ZAXIS_HYBRID || leveltype == ZAXIS_HYBRID_HALF)
                 {
                   if (globs.nvct == 0)
                     {
                       if (zaxisInqVctSize(zaxisID))
                         {
                           globs.nvct = zaxisInqVctSize(zaxisID);
-
-                          if (globs.vct == nullptr)
+                          globs.numHalfLevels = globs.nvct / 2;
+                          if ((int)globs.vct.size() != globs.nvct)
                             {
-                              globs.vct = (double *) Malloc(globs.nvct * sizeof(double));
-                              array_copy(globs.nvct, zaxisInqVctPtr(zaxisID), globs.vct);
+                              globs.vct.resize(globs.nvct);
+                              zaxisInqVct(zaxisID, globs.vct.data());
                             }
                         }
                       else { afterAbort("VCT not defined in inputfile!"); }
                     }
 
-                  if (numlevel != (globs.nvct / 2 - 1))
-                    afterAbort("Number of hybrid levels %d does not match VCT levels %d", numlevel, globs.nvct / 2 - 1);
+                  if (numlevel == globs.numHalfLevels)
+                    {
+                      globs.NumLevelFound--;
+                    }
+                  else if (numlevel != (globs.numHalfLevels - 1))
+                    {
+                      afterAbort("Number of hybrid levels %d does not match VCT levels %d", numlevel, globs.numHalfLevels - 1);
+                    }
 
                   if (globs.Debug)
-                    for (int i = 0; i < globs.nvct / 2; ++i)
-                      fprintf(stderr, " vct: %4d %10.4f %10.4f\n", i, globs.vct[i], globs.vct[i + globs.nvct / 2]);
+                    for (int i = 0; i < globs.numHalfLevels; ++i)
+                      fprintf(stderr, " vct: %4d %10.4f %10.4f\n", i, globs.vct[i], globs.vct[i + globs.numHalfLevels]);
                 }
 
               if (leveltype == ZAXIS_PRESSURE) globs.AnalysisData = true;
@@ -1424,7 +1445,7 @@ after_precntl(AfterControl &globs, struct Variable *vars)
         {
           if (modelInqNamePtr(modelID))
             {
-              if (std::strncmp(modelInqNamePtr(modelID), "ECHAM5", 6) == 0) Source = S_ECHAM5;
+              if (std::strncmp(modelInqNamePtr(modelID), "ECHAM5", 6) == 0) Source = ECHAM5_Source;
               fprintf(stdout, "%s\n", modelInqNamePtr(modelID));
             }
           else
@@ -1434,8 +1455,8 @@ after_precntl(AfterControl &globs, struct Variable *vars)
 
   for (int varID = 0; varID < nvars; ++varID)
     {
-      int gridID, zaxisID, timeID;
-      vlistInqVar(globs.ivlistID, varID, &gridID, &zaxisID, &timeID);
+      int gridID, zaxisID, timeType;
+      vlistInqVar(globs.ivlistID, varID, &gridID, &zaxisID, &timeType);
       auto code = vlistInqVarCode(globs.ivlistID, varID);
       if (code <= 0 || code >= MaxCodes)
         {
@@ -1458,9 +1479,9 @@ after_precntl(AfterControl &globs, struct Variable *vars)
         fprintf(stderr, "Code %3d  Levels = %3d  LevelType = %3d  GridType = %3d\n", code, numlevel, leveltype, gridtype);
     }
 
-  if (globs.Debug) fprintf(stderr, "FieldDim = %d\n", FieldDim);
+  if (globs.Debug) fprintf(stderr, "FieldDim = %d\n", gridSizeMax);
 
-  globs.Field = (double *) Malloc(FieldDim * sizeof(double));
+  globs.varray.resize(gridSizeMax);
 
   if (globs.Debug)
     for (int code = 0; code < MaxCodes; ++code)
@@ -1606,7 +1627,8 @@ after_readVct(AfterControl &globs, const char *vctfile)
     }
 
   globs.nvct = nlines * 2;
-  globs.vct = (double *) Malloc(globs.nvct * sizeof(double));
+  globs.numHalfLevels = globs.nvct / 2;
+  globs.vct.resize(globs.nvct);
 
   rewind(fp);
 
@@ -1618,10 +1640,10 @@ after_readVct(AfterControl &globs, const char *vctfile)
       double va, vb;
       std::sscanf(line, "%d %lg %lg", &n, &va, &vb);
       globs.vct[i] = va;
-      globs.vct[i + globs.nvct / 2] = vb;
+      globs.vct[i + globs.numHalfLevels] = vb;
       i++;
     }
-  fprintf(stdout, "  Read VCT with %d hybrid levels from file %s\n", globs.nvct / 2 - 1, vctfile);
+  fprintf(stdout, "  Read VCT with %d hybrid levels from file %s\n", globs.numHalfLevels - 1, vctfile);
 
   std::fclose(fp);
 }
@@ -1694,9 +1716,9 @@ after_processing(AfterControl &globs, struct Variable *vars)
 
   after_dimcalc(globs);
 
-  globs.rcoslat = (double *) Malloc(globs.Latitudes * sizeof(double));
-  globs.coslat = (double *) Malloc(globs.Latitudes * sizeof(double));
-  globs.DerivationFactor = (double *) Malloc(globs.Latitudes * sizeof(double));
+  globs.rcoslat.resize(globs.Latitudes);
+  globs.coslat.resize(globs.Latitudes);
+  globs.derivationFactor.resize(globs.Latitudes);
 
   if (globs.Type < 50 && globs.AnalysisData)
     {
@@ -1743,12 +1765,12 @@ after_processing(AfterControl &globs, struct Variable *vars)
       vars[GEOPOTENTIAL].needed |= globs.Type >= 30 || vars[SLP].comp || vars[GEOPOTHEIGHT].comp;
     }
 
-  /*  if ( vars[U_WIND].needed || vars[V_WIND].needed ) */
+  // if ( vars[U_WIND].needed || vars[V_WIND].needed )
   if (vars[U_WIND].comp || vars[V_WIND].comp)
     {
-      globs.dv2uv_f1 = (double *) Malloc(globs.DimSP_half * sizeof(double));
-      globs.dv2uv_f2 = (double *) Malloc(globs.DimSP_half * sizeof(double));
-      geninx(globs.Truncation, globs.dv2uv_f1, globs.dv2uv_f2);
+      globs.dv2uv_f1.resize(globs.DimSP_half);
+      globs.dv2uv_f2.resize(globs.DimSP_half);
+      geninx(globs.Truncation, globs.dv2uv_f1.data(), globs.dv2uv_f2.data());
     }
 
   /* --------- */
@@ -1770,17 +1792,27 @@ after_processing(AfterControl &globs, struct Variable *vars)
   streamClose(globs.istreamID);
 }
 
-class ModuleAfterburner
+class Afterburner : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Afterburner",
+    .operators = { { "after", AfterburnerHelp } },
+    .aliases = { { "afterburner", "after" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Afterburner> registration = RegisterEntry<Afterburner>(module);
+
   AfterControl globs = {};
   struct Variable vars[MaxCodes + 5];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     lstdout = !Options::silentMode;
 
     globs.Verbose = Options::cdoVerbose;
@@ -1805,7 +1837,7 @@ public:
       {
         if (globs.Multi > 0) afterAbort("Namelist parameter MULTI works only with one inputfile");
 
-        ifiles = (const char **) Malloc(globs.Nfiles * sizeof(char *));
+        ifiles.resize(globs.Nfiles);
         for (int i = 0; i < globs.Nfiles; ++i) ifiles[i] = cdo_get_stream_name(--nfiles);
         for (int i = 0; i < globs.Nfiles; ++i) printf("files %d %s\n", i + 1, ifiles[i]);
       }
@@ -1821,20 +1853,5 @@ public:
   close()
   {
     FreeMean(vars);
-
-    free(ifile);
-
-    cdo_finish();
   }
 };
-
-void *
-Afterburner(void *process)
-{
-  ModuleAfterburner afterburner;
-  afterburner.init(process);
-  afterburner.run();
-  afterburner.close();
-
-  return nullptr;
-}
diff --git a/src/Arith.cc b/src/Arith.cc
index c78fea83ae0f3c8b714c911bfa4cf1cdde2b4693..68b8f6bffe48b4b3a8632a571090446af6653ab8 100644
--- a/src/Arith.cc
+++ b/src/Arith.cc
@@ -28,22 +28,7 @@
 #include "cdo_fill.h"
 #include "field_functions.h"
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("add",     FieldFunc_Add,     1, nullptr);
-  cdo_operator_add("sub",     FieldFunc_Sub,     1, nullptr);
-  cdo_operator_add("mul",     FieldFunc_Mul,     1, nullptr);
-  cdo_operator_add("div",     FieldFunc_Div,     1, nullptr);
-  cdo_operator_add("min",     FieldFunc_Min,     0, nullptr);
-  cdo_operator_add("max",     FieldFunc_Max,     0, nullptr);
-  cdo_operator_add("atan2",   FieldFunc_Atan2,   0, nullptr);
-  cdo_operator_add("setmiss", FieldFunc_Setmiss, 0, nullptr);
-  // clang-format on
-}
-
-class ModuleArith
+class Arith : public Process
 {
   enum class FillType
   {
@@ -51,14 +36,34 @@ class ModuleArith
     TS,
     VAR,
     VARTS,
-    FILE
+    FULL_FILE
+  };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Arith",
+    .operators = { { "add", FieldFunc_Add, 1, ArithHelp },
+                   { "sub", FieldFunc_Sub, 1, ArithHelp },
+                   { "mul", FieldFunc_Mul, 1, ArithHelp },
+                   { "div", FieldFunc_Div, 1, ArithHelp },
+                   { "min", FieldFunc_Min, 0, ArithHelp },
+                   { "max", FieldFunc_Max, 0, ArithHelp },
+                   { "atan2", FieldFunc_Atan2, 0, ArithHelp },
+                   { "setmiss", FieldFunc_Setmiss, 0, ArithHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
   };
+  inline static RegisterEntry<Arith> registration = RegisterEntry<Arith>(module);
+
   FillType fillType{ FillType::NONE };
   int nlevels2 = 1;
   int levelID2 = -1;
-  Varray2D<size_t> varnmiss;
+  Varray2D<size_t> varnumMissVals;
   Varray2D<double> vardata;
-  std::vector<size_t> varnmiss2;
+  std::vector<size_t> varnumMissVals2;
   Varray<double> vardata2;
 
   CdoStreamID streamID1;
@@ -87,12 +92,8 @@ class ModuleArith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
     bool opercplx = cdo_operator_f2(operatorID);
@@ -200,7 +201,7 @@ public:
     if (fillStream1x || fillType == FillType::VAR || fillType == FillType::VARTS)
       {
         vardata2.resize(gridsizemax * nlevels2);
-        varnmiss2.resize(nlevels2);
+        varnumMissVals2.resize(nlevels2);
       }
 
     if (Options::cdoVerbose) cdo_print("Number of timesteps: file1 %d, file2 %d", ntsteps1, ntsteps2);
@@ -225,7 +226,7 @@ public:
             fieldx2 = &field1;
           }
 
-        if (fillType == FillType::TS) cdo_fill_ts(vlistID2x, vardata, varnmiss);
+        if (fillType == FillType::TS) cdo_fill_ts(vlistID2x, vardata, varnumMissVals);
       }
 
     auto streamsSwaped = (fillType == FillType::TS && vlistID1x != vlistID1);
@@ -266,7 +267,7 @@ public:
         nrecs = cdo_stream_inq_timestep(streamID1, tsID);
 
         nrecs2 = 0;
-        if (tsID == 0 || fillType == FillType::NONE || fillType == FillType::FILE || fillType == FillType::VARTS)
+        if (tsID == 0 || fillType == FillType::NONE || fillType == FillType::FULL_FILE || fillType == FillType::VARTS)
           {
             nrecs2 = cdo_stream_inq_timestep(streamID2, tsID2);
             if (nrecs2 == 0)
@@ -275,23 +276,21 @@ public:
 
                 if (fillType == FillType::NONE && streamID2x == streamID2)
                   {
-                    fillType = FillType::FILE;
+                    fillType = FillType::FULL_FILE;
                     cdo_print("Filling up stream2 >%s< by copying all timesteps.", cdo_get_stream_name(1));
                   }
 
-                if (fillType == FillType::FILE)
+                if (fillType == FillType::FULL_FILE)
                   {
                     cdo_stream_close(streamID2);
 
-                    if (cdo_assert_files_only() == false) cdo_abort("infile2 cannot be a pipe in fill mode!");
+                    if (stream_is_pipe(1)) cdo_abort("infile2 cannot be a pipe in fill mode!");
 
                     streamID2 = cdo_open_read(1);
                     streamID2x = streamID2;
 
                     vlistID2 = cdo_stream_inq_vlist(streamID2);
 
-                    vlist_compare(vlistID1, vlistID2, CmpVlist::Dim);
-
                     tsID2 = 0;
                     nrecs2 = cdo_stream_inq_timestep(streamID2, tsID2);
                     if (nrecs2 == 0) cdo_abort("Empty input stream %s!", cdo_get_stream_name(1));
@@ -315,13 +314,13 @@ public:
             if (lread1)
               {
                 cdo_inq_record(streamID1, &varID, &levelID);
-                cdo_read_record(streamID1, fieldx1->vec_d.data(), &fieldx1->nmiss);
+                cdo_read_record(streamID1, fieldx1->vec_d.data(), &fieldx1->numMissVals);
 
                 if (fillStream1x)
                   {
                     auto gridsize = nwpv * varList1[varID].gridsize;
                     array_copy(gridsize, fieldx1->vec_d.data(), &vardata2[0]);
-                    varnmiss2[0] = fieldx1->nmiss;
+                    varnumMissVals2[0] = fieldx1->numMissVals;
                   }
               }
 
@@ -329,13 +328,13 @@ public:
 
             auto varID2 = varID;
 
-            if (tsID == 0 || fillType == FillType::NONE || fillType == FillType::FILE || fillType == FillType::VARTS)
+            if (tsID == 0 || fillType == FillType::NONE || fillType == FillType::FULL_FILE || fillType == FillType::VARTS)
               {
                 auto lstatus = (nlevels2 > 1) ? (varID == 0) : (recID == 0);
                 if (lstatus || (fillType != FillType::VAR && fillType != FillType::VARTS))
                   {
                     cdo_inq_record(streamID2, &varID2, &levelID2);
-                    cdo_read_record(streamID2, fieldx2->vec_d.data(), &fieldx2->nmiss);
+                    cdo_read_record(streamID2, fieldx2->vec_d.data(), &fieldx2->numMissVals);
                     if (varID != varID2) cdo_abort("Internal error, varIDs of input streams differ!");
                     if (fillStream1x == false && levelID != levelID2)
                       cdo_abort("Internal error, levelIDs of input streams differ!");
@@ -346,14 +345,14 @@ public:
                     auto gridsize = nwpv * varList2[varID].gridsize;
                     auto offset = gridsize * levelID;
                     array_copy(gridsize, fieldx2->vec_d.data(), &vardata[varID][offset]);
-                    varnmiss[varID][levelID] = fieldx2->nmiss;
+                    varnumMissVals[varID][levelID] = fieldx2->numMissVals;
                   }
                 else if (lstatus && (fillType == FillType::VAR || fillType == FillType::VARTS))
                   {
                     auto gridsize = nwpv * varList2[0].gridsize;
                     auto offset = gridsize * levelID2;
                     array_copy(gridsize, fieldx2->vec_d.data(), &vardata2[offset]);
-                    varnmiss2[levelID2] = fieldx2->nmiss;
+                    varnumMissVals2[levelID2] = fieldx2->numMissVals;
                   }
               }
             else if (fillType == FillType::TS)
@@ -361,14 +360,14 @@ public:
                 auto gridsize = nwpv * varList2[varID2].gridsize;
                 auto offset = gridsize * levelID;
                 array_copy(gridsize, &vardata[varID][offset], fieldx2->vec_d.data());
-                fieldx2->nmiss = varnmiss[varID][levelID];
+                fieldx2->numMissVals = varnumMissVals[varID][levelID];
               }
 
             if (fillStream1x)
               {
                 auto gridsize = nwpv * varList1[0].gridsize;
                 array_copy(gridsize, &vardata2[0], fieldx1->vec_d.data());
-                fieldx1->nmiss = varnmiss2[0];
+                fieldx1->numMissVals = varnumMissVals2[0];
               }
 
             fieldx1->grid = varList1[varID].gridID;
@@ -381,7 +380,7 @@ public:
                 auto gridsize = nwpv * varList2[0].gridsize;
                 auto offset = gridsize * levelID2;
                 array_copy(gridsize, &vardata2[offset], fieldx2->vec_d.data());
-                fieldx2->nmiss = varnmiss2[levelID2];
+                fieldx2->numMissVals = varnumMissVals2[levelID2];
                 fieldx2->grid = varList2[0].gridID;
                 fieldx2->missval = varList2[0].missval;
                 fieldx2->nwpv = varList2[0].nwpv;
@@ -399,7 +398,7 @@ public:
               field2_function(field1, field2, operfunc);
 
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, field1.vec_d.data(), field1.nmiss);
+            cdo_write_record(streamID3, field1.vec_d.data(), field1.numMissVals);
           }
 
         tsID++;
@@ -418,18 +417,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID3);
-
-    cdo_finish();
   }
 };
-
-void *
-Arith(void *process)
-{
-  ModuleArith arith;
-  arith.init(process);
-  arith.run();
-  arith.close();
-
-  return nullptr;
-}
diff --git a/src/Arithc.cc b/src/Arithc.cc
index 77997f4c243e39bf50d584728efabfc1baeb0c31..cb68f769e885a906505e11667406a0a469834b47 100644
--- a/src/Arithc.cc
+++ b/src/Arithc.cc
@@ -51,22 +51,26 @@ fill_vars(const VarList &varList, std::vector<bool> &vars)
     }
 }
 
-static void
-add_operators(void)
+class Arithc : public Process
 {
-  // clang-format off
-  cdo_operator_add("addc", FieldFunc_Add, 1, "constant value");
-  cdo_operator_add("subc", FieldFunc_Sub, 1, "constant value");
-  cdo_operator_add("mulc", FieldFunc_Mul, 1, "constant value");
-  cdo_operator_add("divc", FieldFunc_Div, 1, "constant value");
-  cdo_operator_add("minc", FieldFunc_Min, 0, "constant value");
-  cdo_operator_add("maxc", FieldFunc_Max, 0, "constant value");
-  cdo_operator_add("mod",  FieldFunc_Mod, 0, "divisor");
-  // clang-format on
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Arithc",
+    .operators = { { "addc", FieldFunc_Add, 1, "constant value", ArithcHelp },
+                   { "subc", FieldFunc_Sub, 1, "constant value", ArithcHelp },
+                   { "mulc", FieldFunc_Mul, 1, "constant value", ArithcHelp },
+                   { "divc", FieldFunc_Div, 1, "constant value", ArithcHelp },
+                   { "minc", FieldFunc_Min, 0, "constant value", ArithcHelp },
+                   { "maxc", FieldFunc_Max, 0, "constant value", ArithcHelp },
+                   { "mod", FieldFunc_Mod, 0, "divisor", ArithcHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Arithc> registration = RegisterEntry<Arithc>(module);
 
-class ModuleArithc
-{
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -82,12 +86,8 @@ class ModuleArithc
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
     bool opercplx = cdo_operator_f2(operatorID);
@@ -174,18 +174,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Arithc(void *process)
-{
-  ModuleArithc arithc;
-  arithc.init(process);
-  arithc.run();
-  arithc.close();
-
-  return nullptr;
-}
diff --git a/src/Arithdays.cc b/src/Arithdays.cc
index 593155cb1f53bbfba3370ce6a0c8ed11e603c006..be68aa8a89172879b4689609a791eb9e3a1ed82b 100644
--- a/src/Arithdays.cc
+++ b/src/Arithdays.cc
@@ -52,7 +52,7 @@ dayofyear(int calendar, const CdiDateTime &vDateTime)
   return doy;
 }
 
-class ModuleArithdays
+class Arithdays : public Process
 {
   enum
   {
@@ -60,6 +60,23 @@ class ModuleArithdays
     Func_Year,
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Arithdays",
+    .operators = { { "muldpm", FieldFunc_Mul, Func_Month, ArithdaysHelp },
+                   { "divdpm", FieldFunc_Div, Func_Month, ArithdaysHelp },
+                   { "muldpy", FieldFunc_Mul, Func_Year, ArithdaysHelp },
+                   { "divdpy", FieldFunc_Div, Func_Year, ArithdaysHelp },
+                   { "muldoy", FieldFunc_Mul, 0, ArithdaysHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Arithdays> registration = RegisterEntry<Arithdays>(module);
+  int MULDOY;
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -74,20 +91,13 @@ class ModuleArithdays
   VarList varList1;
   Field field;
 
-  int MULDOY;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-               cdo_operator_add("muldpm", FieldFunc_Mul, Func_Month, nullptr);
-               cdo_operator_add("divdpm", FieldFunc_Div, Func_Month, nullptr);
-               cdo_operator_add("muldpy", FieldFunc_Mul, Func_Year, nullptr);
-               cdo_operator_add("divdpy", FieldFunc_Div, Func_Year, nullptr);
-   MULDOY = cdo_operator_add("muldoy", FieldFunc_Mul, 0, nullptr);
+MULDOY = module.get_id("muldoy");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -159,17 +169,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Arithdays(void *process)
-{
-  ModuleArithdays arithdays;
-  arithdays.init(process);
-  arithdays.run();
-  arithdays.close();
-  return nullptr;
-}
diff --git a/src/Arithlat.cc b/src/Arithlat.cc
index 4ddd75497e298511b54ea512474457b88e92c6e0..c4500d937aad4fd2cddcd0090b5cee77a57b3732 100644
--- a/src/Arithlat.cc
+++ b/src/Arithlat.cc
@@ -19,8 +19,23 @@
 #include <mpim_grid.h>
 #include "field_functions.h"
 
-class ModuleArithlat
+class Arithlat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Arithlat",
+    // clang-format off
+    .operators = { { "mulcoslat", FieldFunc_Mul, 0, ArithlatHelp },
+                   { "divcoslat", FieldFunc_Div, 0, ArithlatHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Arithlat> registration = RegisterEntry<Arithlat>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -35,22 +50,17 @@ class ModuleArithlat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("mulcoslat", FieldFunc_Mul, 0, nullptr);
-    cdo_operator_add("divcoslat", FieldFunc_Div, 0, nullptr);
-
-    const auto operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
 
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-    const auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -61,10 +71,11 @@ public:
 
     varList_init(varList1, vlistID1);
 
-    const auto gridsizemax = vlistGridsizeMax(vlistID1);
+    auto gridsizemax = vlistGridsizeMax(vlistID1);
 
     array = Varray<double>(gridsizemax);
   }
+
   void
   run()
   {
@@ -72,7 +83,7 @@ public:
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -82,12 +93,12 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             auto gridID = varList1[varID].gridID;
-            const auto gridsize = varList1[varID].gridsize;
-            const auto missval = varList1[varID].missval;
+            auto gridsize = varList1[varID].gridsize;
+            auto missval = varList1[varID].missval;
 
             if (gridID != gridID0)
               {
@@ -110,7 +121,7 @@ public:
                   for (int i = 0; i < 10; ++i) cdo_print("coslat  %3d  %g", i + 1, scale[i]);
               }
 
-            if (nmiss)
+            if (numMissVals)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   if (!DBL_IS_EQUAL(array[i], missval)) array[i] *= scale[i];
@@ -121,28 +132,17 @@ public:
               }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_write_record(streamID2, array.data(), numMissVals);
           }
 
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Arithlat(void *process)
-{
-  ModuleArithlat arithlat;
-  arithlat.init(process);
-  arithlat.run();
-  arithlat.close();
-  return nullptr;
-}
diff --git a/src/Bitrounding.cc b/src/Bitrounding.cc
index c3b89d808eba0dec778f89edbb6012ccf9dad71e..cb0b2ae0ed2ab10dfadbec44f883a034cf93ad78 100644
--- a/src/Bitrounding.cc
+++ b/src/Bitrounding.cc
@@ -102,7 +102,7 @@ bitround(int nsb, size_t len, Varray<float> &v, float missval)
 
   constexpr uint32_t BIT_XPL_NBR_SGN_FLT = 23;
 
-  // BitRound interprets nsd as number of significant binary digits (bits)
+  // BitRound interprets nsb as number of significant binary digits (bits)
   uint32_t prc_bnr_xpl_rqr = nsb;
 
   uint32_t bit_xpl_nbr_zro = BIT_XPL_NBR_SGN_FLT - prc_bnr_xpl_rqr;
@@ -157,7 +157,7 @@ get_parameter()
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -292,8 +292,19 @@ num_vars_have_numbits(const std::vector<int> &varsNumbits)
   return numVarsHaveNumbits;
 }
 
-class ModuleBitrounding
+class Bitrounding : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Bitrounding",
+    .operators = { { "bitrounding", BitroundingHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Bitrounding> registration = RegisterEntry<Bitrounding>(module);
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -317,12 +328,8 @@ class ModuleBitrounding
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("bitrounding", 0, 0, nullptr);
-
     params = get_parameter();
     if (Options::cdoVerbose) print_parameter(params);
 
@@ -366,6 +373,7 @@ public:
 
     progress::init();
   }
+
   void
   run()
   {
@@ -382,7 +390,7 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            auto fstatus = (ntsteps > 1) ? (tsID + (recID + 1.0) / nrecs) / ntsteps : 1.0;
+            auto fstatus = (ntsteps >= 0) ? (tsID + (recID + 1.0) / nrecs) / ntsteps : 1.0;
             if (!Options::cdoVerbose) progress::update(0, 1, fstatus);
 
             int varID, levelID;
@@ -405,7 +413,7 @@ public:
               {
                 int nsb = (varsNumbits[varID] != -1) ? varsNumbits[varID] : params.numBits;
 
-                if (field.nmiss == 0)
+                if (field.numMissVals == 0)
                   {
                     if (nsb == -1 && (tsID == 0 || params.numSteps == -1))
                       {
@@ -424,7 +432,7 @@ public:
 
                 if (nsb >= 1 && nsb <= 23) bitround(nsb, field.size, field.vec_f, var.missval);
 
-                if (nsb == -1 && field.nmiss > 0 && varsCheckMiss[varID])
+                if (nsb == -1 && field.numMissVals > 0 && varsCheckMiss[varID])
                   {
                     varsCheckMiss[varID] = false;
                     cdo_warning("Missing values unsupported, bitrounding disabled for %s!", var.name);
@@ -475,6 +483,7 @@ public:
         fprintf(stderr, "\n");
       }
   }
+
   void
   close()
   {
@@ -482,16 +491,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-    cdo_finish();
   }
 };
-
-void *
-Bitrounding(void *process)
-{
-  ModuleBitrounding bitrounding;
-  bitrounding.init(process);
-  bitrounding.run();
-  bitrounding.close();
-  return nullptr;
-}
diff --git a/src/CDIread.cc b/src/CDIread.cc
index 3e69d05160ad768f0c94a3c9f4c76ba596d6cb1d..1e211bdc4b53c05e5d28d116d089645760400571 100644
--- a/src/CDIread.cc
+++ b/src/CDIread.cc
@@ -28,8 +28,19 @@ print_stat(const char *sinfo, MemType memtype, int datatype, int filetype, off_t
   cdo_print("%s Read %.1f GB in %.1f seconds, total %.1f MB/s", sinfo, fileSize, tw, (tw > 0) ? 1024 * fileSize / tw : -1);
 }
 
-class ModuleCDIread
+class CDIread : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "CDIread",
+    .operators = { { "cdiread"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<CDIread> registration = RegisterEntry<CDIread>(module);
   MemType memtype = Options::CDO_Memtype;
   int filetype = -1, datatype = -1;
   char sinfo[64];
@@ -41,9 +52,8 @@ class ModuleCDIread
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     sinfo[0] = 0;
 
@@ -100,15 +110,15 @@ public:
                 auto gridsize = varList[varID].gridsize;
                 nvalues += gridsize;
 
-                size_t nmiss;
+                size_t numMissVals;
                 if (memtype == MemType::Float)
                   {
-                    cdo_read_record_f(streamID, farray.data(), &nmiss);
+                    cdo_read_record_f(streamID, farray.data(), &numMissVals);
                     dataSize += gridsize * 4;
                   }
                 else
                   {
-                    cdo_read_record(streamID, darray.data(), &nmiss);
+                    cdo_read_record(streamID, darray.data(), &numMissVals);
                     dataSize += gridsize * 8;
                   }
               }
@@ -135,16 +145,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-void *
-CDIread(void *process)
-{
-
-  ModuleCDIread cdiRead;
-  cdiRead.init(process);
-  cdiRead.run();
-  cdiRead.close();
-  return nullptr;
-}
diff --git a/src/CDItest.cc b/src/CDItest.cc
index af10d800f2526c3c6c2f05ac9eab998d5bb1cf95..c734c2c092ffebcea3c8e22b2337ac9d7e216707 100644
--- a/src/CDItest.cc
+++ b/src/CDItest.cc
@@ -15,21 +15,32 @@
 #include "cdo_timer.h"
 #include "process_int.h"
 
-class ModuleCDItest
+class CDItest : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "CDItest",
+    .operators = { { "ncopy"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<CDItest> registration = RegisterEntry<CDItest>(module);
+  int NCOPY;
   bool dataIsUnchanged;
   int max_copy;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     dataIsUnchanged = false;
     // auto dataIsUnchanged = data_is_unchanged();
 
-    auto NCOPY = cdo_operator_add("ncopy", 0, 0, nullptr);
+    NCOPY = module.get_id("ncopy");
     (void) (NCOPY);  // unused
 
     auto operatorID = cdo_operator_id();
@@ -108,18 +119,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-CDItest(void *process)
-{
-  ModuleCDItest cditest;
-
-  cditest.init(process);
-  cditest.run();
-  cditest.close();
-
-  return nullptr;
-}
diff --git a/src/CDIwrite.cc b/src/CDIwrite.cc
index 70fc8e4c6551c21293d4cff90c9616c7ffc7ff15..15dac3d8549d4a70c4e1a10a3a2bfa09829b528d 100644
--- a/src/CDIwrite.cc
+++ b/src/CDIwrite.cc
@@ -93,7 +93,7 @@ get_parameter(void)
       KVList kvlist;
       // kvlist.name = cdo_module_name();
       kvlist.name = "CDIwrite";
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -127,8 +127,20 @@ verify_parameter(Params &params)
   params.nsteps = std::max(params.nsteps, 1);
 }
 
-class ModuleCDIwrite
+class CDIwrite : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "CDIwrite",
+    .operators = { { "cdiwrite"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 1, NoRestriction },
+  };
+  inline static RegisterEntry<CDIwrite> registration = RegisterEntry<CDIwrite>(module);
+
   MemType memtype = Options::CDO_Memtype;
   int filetype = -1, datatype = -1;
   char sinfo[64] = { 0 };
@@ -148,10 +160,8 @@ class ModuleCDIwrite
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     if (Options::cdoVerbose) cdo_print("parameter: nruns/nvars/nlevs/nsteps/grid/varysteps");
 
     params = get_parameter();
@@ -213,6 +223,7 @@ public:
 
     vlistDefNtsteps(vlistID, params.nsteps);
   }
+
   void
   run()
   {
@@ -296,20 +307,10 @@ public:
 
     if (params.nruns > 1) print_stat("(mean)", memtype, datatype, filetype, nvalues, dataSize, fileSize, runTimeSum / params.nruns);
   }
+
   void
   close()
   {
     vlistDestroy(vlistID);
-
-    cdo_finish();
   }
 };
-void *
-CDIwrite(void *process)
-{
-  ModuleCDIwrite cdiWrite;
-  cdiWrite.init(process);
-  cdiWrite.run();
-  cdiWrite.close();
-  return nullptr;
-}
diff --git a/src/CMOR.cc b/src/CMOR.cc
index 64cc068784ec3f7aea4be12905441feb2c34e37c..2ca53f3c8466d0373efdbb02628afee88398544b 100644
--- a/src/CMOR.cc
+++ b/src/CMOR.cc
@@ -7,6 +7,7 @@
 
 #include <cdi.h>
 #include "julian_date.h"
+#include <stdlib.h>
 #include <signal.h>
 
 #include "process_int.h"
@@ -15,7 +16,6 @@
 #include "cdi_lockedIO.h"
 #include "cdo_options.h"
 #include "varray.h"
-#include "dmemory.h"
 
 #ifdef HAVE_LIBCMOR
 #include <cassert>
@@ -40,7 +40,7 @@ static void
 get_stringcode(int vlistID, int varID, char *varcodestring)
 {
   int varcode = vlistInqVarCode(vlistID, varID);
-  sprintf(varcodestring, "%03d", varcode);
+  std::snprintf(varcodestring, CDI_MAX_NAME, "%03d", varcode);
 }
 
 static int
@@ -50,7 +50,7 @@ get_ifilevalue_code(int vlistID, const char *value, int nvars)
   if (code > 0 && code < 1000)
     {
       char newcode[4];
-      sprintf(newcode, "%03d", code);
+      std::snprintf(newcode, sizeof(newcode), "%03d", code);
       for (int varID = 0; varID < nvars; ++varID)
         {
           char codestring[CDI_MAX_NAME];
@@ -95,10 +95,11 @@ removeDataset()
   getcwd(cwd, sizeof(cwd));
   cwd[strlen(cwd)] = '\0';
   int procID = getpid();
-  char *dataset_path = (char *) Malloc((strlen(cwd) + strlen("/dataset.json") + floor(log10(abs(procID))) + 1 + 1) * sizeof(char));
-  sprintf(dataset_path, "%s/dataset%d.json", cwd, procID);
+  size_t dataPathSize = strlen(cwd) + strlen("/dataset.json") + floor(log10(abs(procID))) + 1 + 1;
+  char *dataset_path = (char *) malloc(dataPathSize);
+  std::snprintf(dataset_path, dataPathSize, "%s/dataset%d.json", cwd, procID);
   remove(dataset_path);
-  Free(dataset_path);
+  free(dataset_path);
 }
 #endif
 
@@ -233,7 +234,7 @@ free_array(char **tofree)
       free(tofree[i]);
       i++;
     }
-  Free(tofree);
+  free(tofree);
 }
 
 static void
@@ -252,10 +253,10 @@ quote_replace(char **values, int nvalues, int i)
 static char **
 parse_string_to_values(char *workfile, char *pline, int *nvalues, const char *keyword)
 {
-  char **values = (char **) Malloc(150 * sizeof(char *));
-  while (isspace((int) *pline)) pline++;
+  char **values = (char **) malloc(150 * sizeof(char *));
+  while (std::isspace((int) *pline)) pline++;
   size_t len = strlen(pline);
-  while (isspace((int) *(pline + (int) len))) len--;
+  while (std::isspace((int) *(pline + (int) len))) len--;
   *(pline + len) = 0;
   if ((int) len == 0) { handleError(workfile, 4, keyword); }
   *nvalues = 0;
@@ -289,12 +290,12 @@ parse_string_to_values(char *workfile, char *pline, int *nvalues, const char *ke
               if (*(pline + i) == 0) { handleError(workfile, 6, pline); }
             }
           i++;
-          if (i != len && *(pline + i) != ',' && !isspace((int) *(pline + i)) && *(pline + i) != 0 && *(pline + i) != '\n')
+          if (i != len && *(pline + i) != ',' && !std::isspace((int) *(pline + i)) && *(pline + i) != 0 && *(pline + i) != '\n')
             {
               handleError(workfile, 11, pline);
             }
         }
-      else if (isspace((int) *(pline + i)))
+      else if (std::isspace((int) *(pline + i)))
         break;
       else if (*(pline + i) == '=')
         {
@@ -512,7 +513,7 @@ static struct mapping *
 construct_var_mapping(int vlistID)
 {
   int nvars_max = vlistNvars(vlistID);
-  struct mapping *vars = (struct mapping *) Malloc((nvars_max + 1) * sizeof(struct mapping));
+  struct mapping *vars = (struct mapping *) malloc((nvars_max + 1) * sizeof(struct mapping));
   vars[0].cdi_varID = CDI_UNDEFID;
   vars[0].cmor_varID = CMOR_UNDEFID;
   vars[0].data = nullptr;
@@ -523,8 +524,8 @@ construct_var_mapping(int vlistID)
 static void
 destruct_var_mapping(struct mapping vars[])
 {
-  for (int i = 0; vars[i].cdi_varID != CDI_UNDEFID; ++i) Free(vars[i].data);
-  Free(vars);
+  for (int i = 0; vars[i].cdi_varID != CDI_UNDEFID; ++i) free(vars[i].data);
+  free(vars);
 }
 
 static struct mapping *
@@ -599,7 +600,7 @@ check_for_charvars(KVList *cmorVarLine, const char * /*cn*/, char * /*miptabfreq
         kv_insert_vals(cmorVarLine, "code", unfilteredComma, true, true);
       else
         kv_insert_vals(cmorVarLine, "name", unfilteredComma, true, true);
-      Free(unfilteredComma);
+      free(unfilteredComma);
       if (Options::cdoVerbose) cdo_print("Successfully replaced identifier string with its values.");
       return cmorVarLine;
     }
@@ -643,11 +644,11 @@ addcharvar(const KeyValues *charvars, int vlistID, const char *key, struct mappi
       if (Options::cdoVerbose)
         cdo_print("In merging variables to an axis:\n          Spatial dimensions are already set. A fourth axis is created.");
       char ids[CDI_MAX_NAME];
-      sprintf(ids, "%d", withnewcharaxis.inputKeys[0].varID);
+      std::snprintf(ids, CDI_MAX_NAME, "%d", withnewcharaxis.inputKeys[0].varID);
       for (int i = 1; i < charvars->nvalues; ++i)
         {
           char tempint[sizeof(int)];
-          sprintf(tempint, "%d", withnewcharaxis.inputKeys[i].varID);
+          std::snprintf(tempint, sizeof(tempint), "%d", withnewcharaxis.inputKeys[i].varID);
           strcat(ids, ",");
           strcat(ids, tempint);
         }
@@ -661,7 +662,7 @@ addcharvar(const KeyValues *charvars, int vlistID, const char *key, struct mappi
               "piped operators so the input file is not clear.",
               cdo_get_stream_name(0));
 
-  const auto streamID2 = streamOpenRead(cdo_get_stream_name(0));
+  auto streamID2 = streamOpenRead(cdo_get_stream_name(0));
   if (ntsteps == -1)
     {
       ntsteps = 0;
@@ -679,7 +680,7 @@ addcharvar(const KeyValues *charvars, int vlistID, const char *key, struct mappi
 
   withnewcharaxis.read_cmor_charvar(axissize, streamID2, oldgridsize);
 
-  var->data = Malloc(ntsteps * axissize[0] * axissize[1] * axissize[2] * sizeof(double));
+  var->data = malloc(ntsteps * axissize[0] * axissize[1] * axissize[2] * sizeof(double));
   for (int i = 0; i < ntsteps * axissize[0] * axissize[1] * axissize[2]; i++)
     {
       if (withnewcharaxis.output.datatype == 'd')
@@ -887,9 +888,9 @@ static char *
 trim(char *s)
 {
   if (s == nullptr) return s;
-  while (*s != '\0' && (isspace(*s) || *s == '"')) s++;
+  while (*s != '\0' && (std::isspace(*s) || *s == '"')) s++;
   int n = strlen(s);
-  while (n > 0 && (isspace(s[n - 1]) || s[n - 1] == '"')) n--;
+  while (n > 0 && (std::isspace(s[n - 1]) || s[n - 1] == '"')) n--;
   s[n] = '\0';
   return s;
 }
@@ -932,7 +933,7 @@ parse_kv_file(KVList *kvl, const char *filename)
       const KeyValues *kvfromlist = kvl->search(kv.key.c_str());
       if (kvfromlist) continue;
 
-      char **values = (char **) Malloc((kv.nvalues + 1) * sizeof(char *));
+      char **values = (char **) malloc((kv.nvalues + 1) * sizeof(char *));
       int k = 0;
       for (k = 0; k < kv.nvalues; k++) values[k] = strdup(kv.values[k].c_str());
 
@@ -969,7 +970,7 @@ check_compare_set(char **finalset, char *attribute, const char *attname, const c
                       "specification '%s'.",
                       attname, *finalset, attribute);
           cdo_print("Attribute '%s' = '%s'", attname, attribute);
-          Free(*finalset);
+          free(*finalset);
           *finalset = strdup(attribute);
         }
     }
@@ -978,19 +979,19 @@ check_compare_set(char **finalset, char *attribute, const char *attname, const c
 static char *
 get_infile_attvalue(int vlistID, int varID, char *name, int type, int len)
 {
-  char *infile_attvalue = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
+  char *infile_attvalue = (char *) malloc(CMOR_MAX_STRING);
   if (type == CDI_DATATYPE_INT32)
     {
-      int *values = (int *) Malloc(len * sizeof(int));
+      int *values = (int *) malloc(len * sizeof(int));
       cdiInqAttInt(vlistID, varID, name, len, &values[0]);
-      sprintf(infile_attvalue, "%i", values[0]);
+      std::snprintf(infile_attvalue, CMOR_MAX_STRING, "%i", values[0]);
       for (int l = 1; l < len; ++l)
         {
           char tempint[sizeof(values[l])];
-          sprintf(tempint, "%i", values[l]);
+          std::snprintf(tempint, sizeof(values[l]), "%i", values[l]);
           strcat(infile_attvalue, tempint);
         }
-      Free(values);
+      free(values);
     }
   else if (type == CDI_DATATYPE_FLT32 || type == CDI_DATATYPE_FLT64)
     {
@@ -1004,15 +1005,20 @@ get_infile_attvalue(int vlistID, int varID, char *name, int type, int len)
           char tempflt[64];
           if (type == CDI_DATATYPE_FLT32)
             {
-              sprintf(tempflt, "%sf", double_to_att_str(Options::CDO_flt_digits, fltstr, sizeof(fltstr), attflt[i]));
+              std::snprintf(tempflt, sizeof(tempflt), "%sf",
+                            double_to_att_str(Options::CDO_flt_digits, fltstr, sizeof(fltstr), attflt[i]));
+            }
+          else
+            {
+              std::snprintf(tempflt, sizeof(tempflt), "%s",
+                            double_to_att_str(Options::CDO_dbl_digits, fltstr, sizeof(fltstr), attflt[i]));
             }
-          else { sprintf(tempflt, "%s", double_to_att_str(Options::CDO_dbl_digits, fltstr, sizeof(fltstr), attflt[i])); }
           if (i)
             strcat(infile_attvalue, tempflt);
           else
-            sprintf(infile_attvalue, "%s", tempflt);
+            std::snprintf(infile_attvalue, CMOR_MAX_STRING, "%s", tempflt);
         }
-      infile_attvalue[CMOR_MAX_STRING] = '\0';
+      infile_attvalue[CMOR_MAX_STRING - 1] = '\0';
       cdo_print("%s", infile_attvalue);
     }
   else
@@ -1026,7 +1032,7 @@ get_infile_attvalue(int vlistID, int varID, char *name, int type, int len)
 static char *
 get_infile_attname(int vlistID, int varID, int natt, int *type, int *len)
 {
-  char *infile_attname = (char *) Malloc(CDI_MAX_NAME * sizeof(char));
+  char *infile_attname = (char *) malloc(CDI_MAX_NAME);
   cdiInqAtt(vlistID, varID, natt, infile_attname, type, len);
   return infile_attname;
 }
@@ -1042,7 +1048,7 @@ get_txtatt(int vlistID, int varID, const char *key)
       int type, len;
       char *infile_attname = get_infile_attname(vlistID, varID, i, &type, &len);
       if (strcmp(infile_attname, key) == 0) { returnvalue = get_infile_attvalue(vlistID, varID, infile_attname, type, len); }
-      Free(infile_attname);
+      free(infile_attname);
     }
   return returnvalue;
 }
@@ -1072,7 +1078,7 @@ get_all_atts(KVList *kvl, int vlistID, const char **infileAttSpecLong, const cha
         {
           kv_insert_vals(kvl, infile_attname, get_infile_attvalue(vlistID, CDI_GLOBAL, infile_attname, type, len), false, false);
         }
-      Free(infile_attname);
+      free(infile_attname);
     }
 
   kv_insert_vals(kvl, "institution", (char *) institutInqLongnamePtr(vlistInqInstitut(vlistID)), false, false);
@@ -1095,7 +1101,7 @@ add_globalhybrids(KVList *kvl, int vlistID)
         {
           char *att = get_txtatt(vlistID, CDI_GLOBAL, infileAttSpecLong[i]);
           if (att) kv_insert_vals(kvl, infileAttSpecShort[i], att, false, false);
-          if (att) Free(att);
+          if (att) free(att);
           i++;
         }
 
@@ -1106,7 +1112,7 @@ add_globalhybrids(KVList *kvl, int vlistID)
           char *att = get_txtatt(vlistID, CDI_GLOBAL, infileAtt[i]);
           if (att) kv_insert_vals(kvl, infileAtt[i], att, false, false);
           i++;
-          if (att) Free(att);
+          if (att) free(att);
         }
     }
   const char *longAtt[] = { "required_time_units",
@@ -1161,9 +1167,9 @@ check_attarray(KVList *kvl, const char **reqAtt, int vlistID)
                   if (Options::cdoVerbose) cdo_print("Attribute (from infile) '%s' is '%s'. ", reqAtt[i], infileatt);
                   const char *infileatts[] = { infileatt };
                   kvl->append(reqAtt[i], infileatts, 1);
-                  Free(infileatt);
+                  free(infileatt);
                 }
-              else if (infileatt) { Free(infileatt); }
+              else if (infileatt) { free(infileatt); }
               return i;
             }
           else
@@ -1184,11 +1190,11 @@ attErr(const char **reqAtt, int errnum)
   char errStr[CMOR_MAX_STRING];
   int i = 1;
 
-  sprintf(errStr,
-          "ERROR! Attribute '%s' is required. Either it is missing, 'notSet', or the value is invalid.\n       "
-          "   Make sure that you have configured all following attributes:\n   "
-          "       %s",
-          reqAtt[errnum], reqAtt[0]);
+  std::snprintf(errStr, sizeof(errStr),
+                "ERROR! Attribute '%s' is required. Either it is missing, 'notSet', or the value is invalid.\n       "
+                "   Make sure that you have configured all following attributes:\n   "
+                "       %s",
+                reqAtt[errnum], reqAtt[0]);
   while (reqAtt[i])
     {
       strcat(errStr, ", ");
@@ -1228,12 +1234,12 @@ check_attr(KVList *kvl, char *project_id, int vlistID)
   if (!kv || kv->nvalues == 0 || kv->values[0] == "notSet")
     {
       const KeyValues *kv_model_id = kvl->search("model_id");
-      char *references = (char *) Malloc(strlen(kv_model_id->values[0].c_str()) + 28);
+      char *references = (char *) malloc(strlen(kv_model_id->values[0].c_str()) + 28);
       std::strcpy(references, "No references available for ");
       strcat(references, kv_model_id->values[0].c_str());
       cdo_print("Attribute 'references' is set to '%s' ", references);
       kv_insert_vals(kvl, "references", references, false, false);
-      Free(references);
+      free(references);
     }
 #endif
   /* Special check for CMIP or CORDEX projects */
@@ -1404,7 +1410,7 @@ check_mem(KVList *kvl, char *project_id)
                 }
             }
           char member[CMOR_MAX_STRING];
-          sprintf(member, "r%ldi%ldp%ld", indexvalues[0], indexvalues[1], indexvalues[2]);
+          std::snprintf(member, sizeof(member), "r%ldi%ldp%ld", indexvalues[0], indexvalues[1], indexvalues[2]);
           kv_insert_vals(kvl, "member", member, true, false);
           kv_insert_vals(kvl, "variant_label", member, true, false);
           indexint = 0;
@@ -1487,7 +1493,7 @@ check_mem(KVList *kvl, char *project_id)
             }
         }
       char member[CMOR_MAX_STRING];
-      sprintf(member, "r%ldi%ldp%ldf%ld", indexvalues[0], indexvalues[1], indexvalues[2], indexvalues[3]);
+      std::snprintf(member, sizeof(member), "r%ldi%ldp%ldf%ld", indexvalues[0], indexvalues[1], indexvalues[2], indexvalues[3]);
       kv_insert_vals(kvl, "member", member, true, false);
       kv_insert_vals(kvl, "variant_label", member, true, false);
     }
@@ -1516,14 +1522,15 @@ read_config_files(KVList *kvl)
   getcwd(cwd, sizeof(cwd));
   cwd[strlen(cwd)] = '\0';
   const char *dotconfig = ".cdocmorinfo";
-  char *workfile = (char *) Malloc((strlen(cwd) + strlen(dotconfig) + 2) * sizeof(char));
-  sprintf(workfile, "%s/%s", cwd, dotconfig);
+  size_t workfileSize = (strlen(cwd) + strlen(dotconfig) + 2);
+  char *workfile = (char *) malloc(workfileSize);
+  std::snprintf(workfile, workfileSize, "%s/%s", cwd, dotconfig);
   if (Options::cdoVerbose) cdo_print("1.2. Try to parse default file: '%s'.", workfile);
   if (parse_kv_file(kvl, workfile) == 0 && Options::cdoVerbose)
     cdo_warning("Default file for keyword 'info': '%s' does not exist.", workfile);
   else if (Options::cdoVerbose)
     cdo_print("1.2. Successfully parsed default file: '%s'.", workfile);
-  Free(workfile);
+  free(workfile);
 
   if (i == 0)
     {
@@ -1554,6 +1561,7 @@ in_list(const std::vector<std::string> &list, const char *needle, int num)
 static int
 get_netcdf_file_action(KVList *kvl, char *proj)
 {
+  (void) proj;
   char *chunk = kv_get_a_val(kvl, "om", "a");
   if (chunk[0] == 'r')
     {
@@ -1615,7 +1623,7 @@ get_cmor_exit_control(KVList *kvl)
 static char *
 get_calendar_ptr(int calendar)
 {
-  char *calendar_ptr = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
+  char *calendar_ptr = (char *) malloc(CMOR_MAX_STRING);
   switch (calendar)
     {
     case CALENDAR_STANDARD: std::strcpy(calendar_ptr, "standard"); break;
@@ -1624,7 +1632,7 @@ get_calendar_ptr(int calendar)
     case CALENDAR_360DAYS: std::strcpy(calendar_ptr, "360_day"); break;
     case CALENDAR_365DAYS: std::strcpy(calendar_ptr, "noleap"); break;
     case CALENDAR_366DAYS: std::strcpy(calendar_ptr, "all_leap"); break;
-    default: Free(calendar_ptr); return nullptr;
+    default: free(calendar_ptr); return nullptr;
     }
   return calendar_ptr;
 }
@@ -1660,16 +1668,17 @@ get_calendar_int(char *calendar)
 static char *
 get_time_units(int taxisID)
 {
-  char *units = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
+  char *units = (char *) malloc(CMOR_MAX_STRING);
   int timeunit = taxisInqTunit(taxisID);
   int year, month, day, hour, minute, second, ms;
-  const auto rDateTime = taxisInqRdatetime(taxisID);
+  auto rDateTime = taxisInqRdatetime(taxisID);
   cdiDate_decode(rDateTime.date, &year, &month, &day);
   cdiTime_decode(rDateTime.time, &hour, &minute, &second, &ms);
   if (timeunit == TUNIT_QUARTER || timeunit == TUNIT_30MINUTES) timeunit = TUNIT_MINUTE;
   if (timeunit == TUNIT_3HOURS || timeunit == TUNIT_6HOURS || timeunit == TUNIT_12HOURS) timeunit = TUNIT_HOUR;
 
-  sprintf(units, "%s since %d-%d-%d %02d:%02d:%02d", tunitNamePtr(timeunit), year, month, day, hour, minute, second);
+  std::snprintf(units, CMOR_MAX_STRING, "%s since %d-%d-%d %02d:%02d:%02d", tunitNamePtr(timeunit), year, month, day, hour, minute,
+                second);
   return units;
 }
 
@@ -1792,7 +1801,7 @@ get_time_method(KVList *kvl, int vlistID, int varID, char *cmor_time_name, char
                         time_method, varID);
           std::strcpy(cmor_time_name, "time \0");
         }
-      Free(time_method);
+      free(time_method);
     }
   if (Options::cdoVerbose) cdo_print("Successfully determined time_axis = '%d'.", *time_axis);
 }
@@ -1819,7 +1828,7 @@ get_branch_times(KVList *kvl, int calendar, char *time_units, char *project_id)
   char *btip = kv_get_a_val(kvl, "branch_time_in_parent", nullptr);
   char *btic = kv_get_a_val(kvl, "branch_time_in_child", nullptr);
   char *ptu = kv_get_a_val(kvl, "parent_time_units", nullptr);
-  double *branch_time = (double *) Malloc(2 * sizeof(double));
+  double *branch_time = (double *) malloc(2 * sizeof(double));
   branch_time[0] = 0.0;
   branch_time[1] = 0.0;
 
@@ -1844,14 +1853,14 @@ get_branch_times(KVList *kvl, int calendar, char *time_units, char *project_id)
             }
 
           int parenttimeunit;
-          const auto parentsDateTime = get_taxis(ptu, &parenttimeunit);
-          const auto parentstartdate = julianDate_encode(calendar, parentsDateTime);
-          const auto parentbranchdate = julianDate_encode(calendar, branchDateTimes[0]);
+          auto parentsDateTime = get_taxis(ptu, &parenttimeunit);
+          auto parentstartdate = julianDate_encode(calendar, parentsDateTime);
+          auto parentbranchdate = julianDate_encode(calendar, branchDateTimes[0]);
 
           int childtimeunit;
-          const auto childsDateTime = get_taxis(time_units, &childtimeunit);
-          const auto childstartdate = julianDate_encode(calendar, childsDateTime);
-          const auto childbranchdate = julianDate_encode(calendar, branchDateTimes[1]);
+          auto childsDateTime = get_taxis(time_units, &childtimeunit);
+          auto childstartdate = julianDate_encode(calendar, childsDateTime);
+          auto childbranchdate = julianDate_encode(calendar, branchDateTimes[1]);
 
           /* If time unit is always "days since.." */
           branch_time[0] = julianDate_to_seconds(julianDate_sub(parentbranchdate, parentstartdate)) / 86400;
@@ -1942,8 +1951,9 @@ static const char *
 copyCV(char *directory)
 {
   const char *cvwithout = "CMIP6_CV_without_prefix.json";
-  char *cvname = (char *) Malloc((strlen(directory) + strlen(cvwithout) + 2) * sizeof(char));
-  sprintf(cvname, "%s/%s", directory, cvwithout);
+  size_t len = strlen(directory) + strlen(cvwithout) + 2;
+  char *cvname = (char *) malloc(len);
+  std::snprintf(cvname, len, "%s/%s", directory, cvwithout);
   if (Options::cdoVerbose) cdo_print("Check whether a CV without tracking prefix check exists.");
   if (file_exist((const char *) cvname, false, "CV", false))
     return cvwithout;
@@ -1952,7 +1962,8 @@ copyCV(char *directory)
       if (Options::cdoVerbose) cdo_print("Try to create a CV without tracking prefix check with program 'sed'.");
 
       char command[CDI_MAX_NAME];
-      sprintf(command, "sed 's/\"hdl:21.14100\\/\\.\\*\"/\"\\^\\.*\"/' %s/CMIP6_CV.json >%s", directory, cvname);
+      std::snprintf(command, sizeof(command), "sed 's/\"hdl:21.14100\\/\\.\\*\"/\"\\^\\.*\"/' %s/CMIP6_CV.json >%s", directory,
+                    cvname);
       int dir_err = system(command);
       if (dir_err != 0)
         {
@@ -1961,7 +1972,7 @@ copyCV(char *directory)
                       "This can be due to missing 'sed' program or missing write permissions. "
                       "Continue with tracking prefix. "
                       "Consider that the output tracking id needs to be registered as a PID.");
-          sprintf(cvname, "CMIP6_CV.json");
+          std::snprintf(cvname, len, "CMIP6_CV.json");
           return cvname;
         }
       if (Options::cdoVerbose) cdo_print("Successfully created a CV without tracking prefix check.");
@@ -2007,15 +2018,15 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
 
       char cordexDir[CDI_MAX_NAME];
       char cordexFileTem[CDI_MAX_NAME];
-      sprintf(cordexDir, "%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s", kv_get_a_val(kvl, "dr", "./"), project_id,
-              kv_get_a_val(kvl, "product", nullptr), kv_get_a_val(kvl, "CORDEX_domain", nullptr),
-              kv_get_a_val(kvl, "institute_id", nullptr), kv_get_a_val(kvl, "driving_model_id", nullptr),
-              kv_get_a_val(kvl, "experiment_id", nullptr), kv_get_a_val(kvl, "member", nullptr),
-              kv_get_a_val(kvl, "model_id", nullptr), kv_get_a_val(kvl, "rcm_version_id", nullptr), freq);
-      sprintf(cordexFileTem, "%s_%s_%s_%s_%s_%s_%s", kv_get_a_val(kvl, "CORDEX_domain", nullptr),
-              kv_get_a_val(kvl, "driving_model_id", nullptr), kv_get_a_val(kvl, "experiment_id", nullptr),
-              kv_get_a_val(kvl, "member", nullptr), kv_get_a_val(kvl, "model_id", nullptr),
-              kv_get_a_val(kvl, "rcm_version_id", nullptr), freq);
+      std::snprintf(cordexDir, CDI_MAX_NAME, "%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s", kv_get_a_val(kvl, "dr", "./"), project_id,
+                    kv_get_a_val(kvl, "product", nullptr), kv_get_a_val(kvl, "CORDEX_domain", nullptr),
+                    kv_get_a_val(kvl, "institute_id", nullptr), kv_get_a_val(kvl, "driving_model_id", nullptr),
+                    kv_get_a_val(kvl, "experiment_id", nullptr), kv_get_a_val(kvl, "member", nullptr),
+                    kv_get_a_val(kvl, "model_id", nullptr), kv_get_a_val(kvl, "rcm_version_id", nullptr), freq);
+      std::snprintf(cordexFileTem, CDI_MAX_NAME, "%s_%s_%s_%s_%s_%s_%s", kv_get_a_val(kvl, "CORDEX_domain", nullptr),
+                    kv_get_a_val(kvl, "driving_model_id", nullptr), kv_get_a_val(kvl, "experiment_id", nullptr),
+                    kv_get_a_val(kvl, "member", nullptr), kv_get_a_val(kvl, "model_id", nullptr),
+                    kv_get_a_val(kvl, "rcm_version_id", nullptr), freq);
 
       kv_insert_vals(kvl, "dr", (char *) "./", true, false);
       kv_insert_vals(kvl, "cordexDir", cordexDir, true, false);
@@ -2109,9 +2120,10 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
   if (strcmp(kv_get_a_val(kvl, "kaa", "n"), "y") == 0)
     {
       char notincluded[2048];
-      std::strcpy(notincluded,
-             "The following attributes are not included in the global attributes list.\n          Reasons can be: 1. Attribute is "
-             "an internal keyword 2. No valaue is available 3. CMOR creates the attribute itself:\n          ");
+      std::strcpy(
+          notincluded,
+          "The following attributes are not included in the global attributes list.\n          Reasons can be: 1. Attribute is "
+          "an internal keyword 2. No valaue is available 3. CMOR creates the attribute itself:\n          ");
       size_t inilen = strlen(notincluded);
       size_t strlens = inilen;
       for (auto &kv : *kvl)
@@ -2149,15 +2161,15 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
           char *filename = kv_get_a_val(kvl, "dj", nullptr); */
 
     size_t cwdsize = 1024;
-    char *cwd = (char *) Malloc((cwdsize + 1) * sizeof(char));
+    char *cwd = (char *) malloc(cwdsize + 1);
     getcwd(cwd, cwdsize);
     cwd[strlen(cwd)] = '\0';
     int procID = getpid();
     FILE *dataset_json;
-    char *dataset_path
-        = (char *) Malloc((strlen(cwd) + strlen("/dataset.json") + floor(log10(abs(procID))) + 1 + 1) * sizeof(char));
+    size_t len = strlen(cwd) + strlen("/dataset.json") + floor(log10(abs(procID))) + 1 + 1;
+    char *dataset_path = (char *) malloc(len);
 
-    sprintf(dataset_path, "%s/dataset%d.json", cwd, procID);
+    std::snprintf(dataset_path, len, "%s/dataset%d.json", cwd, procID);
     dataset_json = std::fopen(dataset_path, "w+");
     if (!dataset_json)
       cdo_abort("ERROR (infile: '%s')! In preparing cmor_dataset:\n          Could not open a dataset file '%s' for cmor_dataset.",
@@ -2173,7 +2185,7 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
               {
                 int linelen = strlen(kv.key.c_str()) + strlen(kv.values[0].c_str()) + 10;
                 std::vector<char> line(linelen);
-                sprintf(line.data(), "\"%s\" : \"%s\",\n", kv.key.c_str(), kv.values[0].c_str());
+                std::snprintf(line.data(), linelen, "\"%s\" : \"%s\",\n", kv.key.c_str(), kv.values[0].c_str());
                 fputs((const char *) line.data(), dataset_json);
               }
           }
@@ -2204,7 +2216,7 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
               {
                 int linelen = strlen(allneeded[i]) + strlen(tmp) + 10;
                 std::vector<char> line(linelen);
-                sprintf(line.data(), "\"%s\" : \"%s\",\n", allneeded[i], tmp);
+                std::snprintf(line.data(), linelen, "\"%s\" : \"%s\",\n", allneeded[i], tmp);
                 fputs((const char *) line.data(), dataset_json);
               }
             i++;
@@ -2213,8 +2225,8 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
 
     char branch_time_in_parent[CMOR_MAX_STRING];
     char branch_time_in_child[CMOR_MAX_STRING];
-    sprintf(branch_time_in_parent, "%.12f", branch_times[0]);
-    sprintf(branch_time_in_child, "%.12f", branch_times[1]);
+    std::snprintf(branch_time_in_parent, CMOR_MAX_STRING, "%.12f", branch_times[0]);
+    std::snprintf(branch_time_in_child, CMOR_MAX_STRING, "%.12f", branch_times[1]);
 
     /* CMOR internal */
     fputs("\"outpath\" : \"", dataset_json);
@@ -2306,9 +2318,9 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
     cmf = cmor_dataset_json(dataset_path);
     if (cmf != 0) cdo_abort("ERROR (infile: '%s')! Function cmor_dataset_json failed!", cdo_get_stream_name(0));
 
-    Free(dataset_path);
+    free(dataset_path);
     removeDataset();
-    /*      Free(freq); */
+    /*      free(freq); */
   }
 
 #else
@@ -2319,8 +2331,8 @@ setup_dataset(KVList *kvl, CdoStreamID streamID, int *calendar, char *project_id
             "CMOR_VERSION_MINOR are not available.",
             cdo_get_stream_name(0));
 #endif
-  Free(calendarptr);
-  Free(branch_times);
+  free(calendarptr);
+  free(branch_times);
   if (Options::cdoVerbose) cdo_print("6. Successfully finished cmor_setup and cmor_dataset.");
 }
 
@@ -2342,10 +2354,10 @@ get_zcell_bounds(int zaxisID, double *zcell_bounds, double *levels, int zsize)
 {
   bool selfGenerated = false;
   double *lbounds;
-  lbounds = (double *) Malloc(zsize * sizeof(double));
+  lbounds = (double *) malloc(zsize * sizeof(double));
   zaxisInqLbounds(zaxisID, lbounds);
   double *ubounds;
-  ubounds = (double *) Malloc(zsize * sizeof(double));
+  ubounds = (double *) malloc(zsize * sizeof(double));
   zaxisInqUbounds(zaxisID, ubounds);
   if (!lbounds || !ubounds || std::pow((ubounds[1] - ubounds[0]), 2) < 0.001 || std::pow((lbounds[1] - lbounds[0]), 2) < 0.001)
     {
@@ -2369,8 +2381,8 @@ get_zcell_bounds(int zaxisID, double *zcell_bounds, double *levels, int zsize)
         zcell_bounds[2 * zsize - 1] = levels[zsize - 1] + (levels[zsize - 1] - zcell_bounds[2 * zsize - 2]);
       selfGenerated = false;
     }
-  Free(lbounds);
-  Free(ubounds);
+  free(lbounds);
+  free(ubounds);
   return selfGenerated;
 }
 
@@ -2384,7 +2396,7 @@ get_zhybrid_half(int zaxisID, double *p0, double *alev_val, double *b_val, doubl
               " to calculate the formula of a hybrid sigma pressure axis:"
               "\n          p = ap + b *ps",
               cdo_get_stream_name(0));
-  double *vct = (double *) Malloc(vctsize * sizeof(double));
+  double *vct = (double *) malloc(vctsize * sizeof(double));
   zaxisInqVct(zaxisID, vct);
   for (int i = 0; i < zsize; ++i)
     {
@@ -2392,7 +2404,7 @@ get_zhybrid_half(int zaxisID, double *p0, double *alev_val, double *b_val, doubl
       b_val[i] = vct[zsize + i];
     }
   for (int i = 0; i < zsize; ++i) { alev_val[i] = ap_val[i] / p0[0] + b_val[i]; }
-  Free(vct);
+  free(vct);
 }
 
 static void
@@ -2406,7 +2418,7 @@ get_zhybrid(int zaxisID, double *p0, double *alev_val, double *alev_bnds, double
               " to calculate the formula of a hybrid sigma pressure axis or a hybrid height axis:"
               "\n          p = ap + b * ps||orog",
               cdo_get_stream_name(0));
-  double *vct = (double *) Malloc(vctsize * sizeof(double));
+  double *vct = (double *) malloc(vctsize * sizeof(double));
   zaxisInqVct(zaxisID, vct);
   for (int i = 0; i < (zsize + 1); ++i)
     {
@@ -2421,7 +2433,7 @@ get_zhybrid(int zaxisID, double *p0, double *alev_val, double *alev_bnds, double
       alev_bnds[i] = ap_bnds[i] / p0[0] + b_bnds[i];
     }
   alev_bnds[zsize] = ap_bnds[zsize] / p0[0] + b_bnds[zsize];
-  Free(vct);
+  free(vct);
 }
 
 static size_t
@@ -2438,28 +2450,28 @@ get_charvals_and_bnds(KVList *kvl, char *chardim, std::vector<std::string> &fval
                       int *nofvals, int *nofbnds, char *cmor_name)
 {
   bool fivedim = true;
-  char *charvalstring = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
-  sprintf(charvalstring, "char_axis_%s_%s", chardim, cmor_name);
+  char charvalstring[CMOR_MAX_STRING];
+  std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s_%s", chardim, cmor_name);
   fvalss = kv_get_vals(kvl, charvalstring, nofvals);
   if (!fvalss.size())
     {
-      if (Options::cdoVerbose) cdo_print("Start to register char_axis_%s", chardim);
-      sprintf(charvalstring, "char_axis_%s", chardim);
-      fvalss = kv_get_vals(kvl, charvalstring, nofvals);
-      fivedim = false;
+    if (Options::cdoVerbose) cdo_print("Start to register char_axis_%s", chardim);
+    std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s", chardim);
+    fvalss = kv_get_vals(kvl, charvalstring, nofvals);
+    fivedim = false;
     }
   else
     {
-      if (Options::cdoVerbose) cdo_print("Start to register char_axis_%s_%s", chardim, cmor_name);
+    if (Options::cdoVerbose) cdo_print("Start to register char_axis_%s_%s", chardim, cmor_name);
     }
   if (!fvalss.size())
     cdo_warning("You specify variables to merge to an axis and the axis name but its values are missing!"
                 "\n          Specify its values via attribute 'char_axis_$name' in infofile.");
 
   if (fivedim)
-    sprintf(charvalstring, "char_axis_%s_%s_bounds", chardim, cmor_name);
+    std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s_%s_bounds", chardim, cmor_name);
   else
-    sprintf(charvalstring, "char_axis_%s_bounds", chardim);
+    std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s_bounds", chardim);
   fbndss = kv_get_vals(kvl, charvalstring, nofbnds);
   if (!fbndss.size())
     if (Options::cdoVerbose)
@@ -2468,17 +2480,15 @@ get_charvals_and_bnds(KVList *kvl, char *chardim, std::vector<std::string> &fval
                 "\n          Specify its bounds via attribute 'char_axis_$name_bounds' in infofile.");
 
   if (fivedim)
-    sprintf(charvalstring, "char_axis_%s_%s_units", chardim, cmor_name);
+    std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s_%s_units", chardim, cmor_name);
   else
-    sprintf(charvalstring, "char_axis_%s_units", chardim);
+    std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s_units", chardim);
   *funits = kv_get_a_val(kvl, charvalstring, "");
   if (!*funits)
     if (Options::cdoVerbose)
       cdo_print("You specified variables to merge to an axis, the axis name and its values."
                 "\n          Note that units are required if the axis has digital values."
                 "\n          Specify its units via attribute 'char_axis_$name_units' in infofile.");
-
-  Free(charvalstring);
 }
 
 static void
@@ -2486,23 +2496,24 @@ register_char_axis(int numchar, const std::vector<std::string> &charvals, int *a
 {
   if (Options::cdoVerbose) cdo_print("Start to register a character axis.");
   size_t maxlen = get_strmaxlen(charvals, numchar);
-  void *charcmor = (void *) Malloc((numchar * maxlen + 1) * sizeof(char));
-  sprintf((char *) charcmor, "%.*s", (int) strlen(charvals[0].c_str()), charvals[0].c_str());
+  size_t len = numchar * maxlen + 1;
+  void *charcmor = (void *) malloc(len);
+  std::snprintf((char *) charcmor, len, "%.*s", (int) strlen(charvals[0].c_str()), charvals[0].c_str());
   std::vector<char> blanks(maxlen);
   for (size_t i = 0; i < maxlen; ++i) blanks[i] = ' ';
   char tempb[CMOR_MAX_STRING];
-  sprintf(tempb, "%.*s", (int) (maxlen - strlen(charvals[0].c_str())), blanks.data());
+  std::snprintf(tempb, CMOR_MAX_STRING, "%.*s", (int) (maxlen - strlen(charvals[0].c_str())), blanks.data());
   strcat((char *) charcmor, tempb);
   for (int i = 1; i < numchar; ++i)
     {
       strcat((char *) charcmor, charvals[i].c_str());
       char tempblanks[CMOR_MAX_STRING];
-      sprintf(tempblanks, "%.*s", (int) (maxlen - strlen(charvals[i].c_str())), blanks.data());
+      std::snprintf(tempblanks, CMOR_MAX_STRING, "%.*s", (int) (maxlen - strlen(charvals[i].c_str())), blanks.data());
       strcat((char *) charcmor, tempblanks);
     }
   int cmf = cmor_axis(new_axis_id(axis_ids), chardim, (char *) "", numchar, (void *) charcmor, 'c', NULL, maxlen, NULL);
   if (cmf != 0) cdo_abort("ERROR (infile: '%s')! Function cmor_axis failed!", cdo_get_stream_name(0));
-  Free(charcmor);
+  free(charcmor);
   if (Options::cdoVerbose) cdo_print("Successfully registered a character axis.");
 }
 
@@ -2510,6 +2521,8 @@ static void
 register_fourth_axis(KVList *kvl, int vlistID, int varID, char *varname, int *axis_ids, char *project_id, int miptab_freq,
                      int *mergeIDs)
 {
+  (void) project_id;
+  (void) miptab_freq;
   char *fa = get_txtatt(vlistID, varID, "merge_axis");
   char *chardimatt = kv_get_a_val(kvl, "ca", NULL);
   char *chardim = get_txtatt(vlistID, varID, "character_axis");
@@ -2546,7 +2559,7 @@ register_fourth_axis(KVList *kvl, int vlistID, int varID, char *varname, int *ax
                           "values for this axis: '%d'.",
                           cdo_get_stream_name(0), nvalues, chardim, nofvals);
               free_array(idss);
-              Free(fa);
+              free(fa);
             }
           else if (nofvals == zaxisInqSize(vlistInqVarZaxis(vlistID, varID)))
             {
@@ -2567,7 +2580,7 @@ register_fourth_axis(KVList *kvl, int vlistID, int varID, char *varname, int *ax
           int i = 0;
           if (fbndss.size())
             {
-              fbnds = (double *) Malloc(nofbnds * sizeof(double));
+              fbnds = (double *) malloc(nofbnds * sizeof(double));
               for (i = 0; i < nofbnds; ++i)
                 {
                   int scanreturn = std::sscanf(fbndss[i].c_str(), "%lf", &fbnds[i]);
@@ -2585,7 +2598,7 @@ register_fourth_axis(KVList *kvl, int vlistID, int varID, char *varname, int *ax
           i = 0;
           if (nofvals > 0)
             {
-              fvals = (double *) Malloc(nofvals * sizeof(double));
+              fvals = (double *) malloc(nofvals * sizeof(double));
               for (i = 0; i < nofvals; ++i)
                 {
                   int scanreturn = std::sscanf(fvalss[i].c_str(), "%lf", &fvals[i]);
@@ -2611,9 +2624,9 @@ register_fourth_axis(KVList *kvl, int vlistID, int varID, char *varname, int *ax
                           chardim);
             }
           else { register_char_axis(nofvals, fvalss, axis_ids, chardim); }
-          if (fvals) Free(fvals);
-          if (fbnds) Free(fbnds);
-          Free(chardim);
+          if (fvals) free(fvals);
+          if (fbnds) free(fbnds);
+          free(chardim);
         }
     }
   else if (Options::cdoVerbose)
@@ -2643,12 +2656,12 @@ registerPsid(struct mapping vars[], int psindex, int vlistID)
   if (vlistInqVarDatatype(vlistID, psindex) == CDI_DATATYPE_FLT64)
     {
       var->datatype = 'd';
-      var->data = Malloc(gridsize * sizeof(double));
+      var->data = malloc(gridsize * sizeof(double));
     }
   else
     {
       var->datatype = 'f';
-      var->data = Malloc(gridsize * sizeof(float));
+      var->data = malloc(gridsize * sizeof(float));
     }
   int psID = getRegisteredPsid(vars, psindex);
   if (Options::cdoVerbose) cdo_print("9. Successfully registered surface pressure '%d'.", psID);
@@ -2681,12 +2694,12 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
 
   if (zsize > 1)
     {
-      levels = (double *) Malloc(zsize * sizeof(double));
+      levels = (double *) malloc(zsize * sizeof(double));
       zaxisInqLevels(zaxisID, levels);
       double *zcell_bounds;
-      zcell_bounds = (double *) Malloc(2 * zsize * sizeof(double));
+      zcell_bounds = (double *) malloc(2 * zsize * sizeof(double));
       bool selfGenerated = get_zcell_bounds(zaxisID, zcell_bounds, levels, zsize);
-      char *zaxisname = (char *) Malloc(CDI_MAX_NAME * sizeof(char));
+      char zaxisname[CDI_MAX_NAME];
       length = CDI_MAX_NAME;
       cdiInqKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_NAME, zaxisname, &length);
       if (zaxisInqType(zaxisID) == ZAXIS_PRESSURE)
@@ -2761,24 +2774,24 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
           int vctsize = zaxisInqVctSize(zaxisID);
           if (2 * zsize == vctsize) zaxistype = ZAXIS_HYBRID_HALF;
           double *alev_val, *alev_bnds = nullptr, *ap_val, *ap_bnds = nullptr, *b_val, *b_bnds = nullptr;
-          double *p0 = (double *) Malloc(sizeof(double));
+          double *p0 = (double *) malloc(sizeof(double));
           p0[0] = 101325.0;
           if (strcmp(zaxis, "hybrid_height") == 0) p0[0] = 1;
 
           if (zaxistype == ZAXIS_HYBRID)
             {
-              alev_val = (double *) Malloc(zsize * sizeof(double));
-              alev_bnds = (double *) Malloc((zsize + 1) * sizeof(double));
-              ap_val = (double *) Malloc(zsize * sizeof(double));
-              ap_bnds = (double *) Malloc((zsize + 1) * sizeof(double));
-              b_val = (double *) Malloc(zsize * sizeof(double));
-              b_bnds = (double *) Malloc((zsize + 1) * sizeof(double));
+              alev_val = (double *) malloc(zsize * sizeof(double));
+              alev_bnds = (double *) malloc((zsize + 1) * sizeof(double));
+              ap_val = (double *) malloc(zsize * sizeof(double));
+              ap_bnds = (double *) malloc((zsize + 1) * sizeof(double));
+              b_val = (double *) malloc(zsize * sizeof(double));
+              b_bnds = (double *) malloc((zsize + 1) * sizeof(double));
             }
           else
             {
-              alev_val = (double *) Malloc(zsize * sizeof(double));
-              ap_val = (double *) Malloc(zsize * sizeof(double));
-              b_val = (double *) Malloc(zsize * sizeof(double));
+              alev_val = (double *) malloc(zsize * sizeof(double));
+              ap_val = (double *) malloc(zsize * sizeof(double));
+              b_val = (double *) malloc(zsize * sizeof(double));
             }
 
           char *mtproof = kv_get_a_val(kvl, "mtproof", nullptr);
@@ -2829,7 +2842,7 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
 
               int tsID = 0;
 
-              const auto streamID2 = streamOpenRead(cdo_get_stream_name(0));
+              auto streamID2 = streamOpenRead(cdo_get_stream_name(0));
               int vlistID2 = streamInqVlist(streamID2);
               psindex = getVarIDToMap(vlistID2, vlistNvars(vlistID2), "name", "orog");
               if (vlistInqVarDatatype(vlistID2, psindex) == CDI_DATATYPE_FLT64) orogtype = 'd';
@@ -2839,9 +2852,9 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
               Varray<double> buffer(gridsize);
 
               if (vlistInqVarDatatype(vlistID2, psindex) == CDI_DATATYPE_FLT64)
-                orogdata = Malloc(gridsize * sizeof(double));
+                orogdata = malloc(gridsize * sizeof(double));
               else
-                orogdata = Malloc(gridsize * sizeof(float));
+                orogdata = malloc(gridsize * sizeof(float));
 
               while (true)
                 {
@@ -2851,12 +2864,12 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
                   while (nrecs--)
                     {
                       int varIDrw, levelIDrw;
-                      size_t nmiss;
+                      size_t numMissVals;
                       streamInqRecord(streamID2, &varIDrw, &levelIDrw);
                       if (varIDrw == psindex)
                         {
                           cdo_print("read the record");
-                          streamReadRecord(streamID2, buffer.data(), &nmiss);
+                          streamReadRecord(streamID2, buffer.data(), &numMissVals);
                           for (size_t i = 0; i < gridsize; ++i)
                             {
                               if (vlistInqVarDatatype(vlistID2, psindex) == CDI_DATATYPE_FLT64)
@@ -2888,7 +2901,7 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
           int lev_id = axis_ids[count_axis_ids(axis_ids) - 1];
           int lev_id_array[2];
           lev_id_array[0] = lev_id;
-          int hharray[count_axis_ids(axis_ids) - 2];
+          std::vector<int> hharray(count_axis_ids(axis_ids) - 2);
           for (int i = 0; i < count_axis_ids(axis_ids) - 2; i++) hharray[i] = axis_ids[i + 1];
 
           if (zaxistype == ZAXIS_HYBRID)
@@ -2908,7 +2921,7 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
                                  (void *) b_bnds);
               if (strcmp(zaxis, "hybrid_height") == 0)
                 {
-                  cmf = cmor_zfactor(zfactor_id, lev_id, (char *) "orog", (char *) "m", count_axis_ids(axis_ids) - 2, hharray,
+                  cmf = cmor_zfactor(zfactor_id, lev_id, (char *) "orog", (char *) "m", count_axis_ids(axis_ids) - 2, hharray.data(),
                                      orogtype, (void *) orogdata, nullptr);
                 }
               else if (time_method[0] == 'p')
@@ -2949,15 +2962,15 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
                 cmf = cmor_zfactor(zfactor_id, lev_id, (char *) "ps", (char *) "Pa", count_axis_ids(axis_ids) - 1, axis_ids,
                                    vars[psID].datatype, nullptr, nullptr);
             }
-          Free(alev_val);
-          Free(ap_val);
-          Free(b_val);
+          free(alev_val);
+          free(ap_val);
+          free(b_val);
           if (zaxistype == ZAXIS_HYBRID)
             {
-              if (strcmp(zaxis, "hybrid_height") == 0) Free(orogdata);
-              Free(ap_bnds);
-              Free(alev_bnds);
-              Free(b_bnds);
+              if (strcmp(zaxis, "hybrid_height") == 0) free(orogdata);
+              free(ap_bnds);
+              free(alev_bnds);
+              free(b_bnds);
             }
         }
       else if (zaxisInqType(zaxisID) == ZAXIS_DEPTH_BELOW_SEA || zaxisInqType(zaxisID) == ZAXIS_DEPTH_BELOW_LAND)
@@ -3027,13 +3040,12 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
             cdo_abort("ERROR (infile: '%s')! In registration of a vertical axis:\n          Z-axis type %d with name '%s' not yet "
                       "enabled.",
                       cdo_get_stream_name(0), zaxisInqType(zaxisID), zaxisname);
-          Free(zaxisname);
         }
       else
         cdo_abort("ERROR (infile: '%s')! In registration of a vertical axis:\n          Invalid Z-axis type %d . ",
                   cdo_get_stream_name(0), zaxisInqType(zaxisID));
-      Free(zcell_bounds);
-      Free(levels);
+      free(zcell_bounds);
+      free(levels);
     }
 
   else if (zsize == 1 && strcmp(zaxis, "notSet") != 0)
@@ -3047,11 +3059,11 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
       char *szc_value = kv_get_a_val(kvl, (const char *) zaxis, nullptr);
       if (szc_value)
         {
-          levels = (double *) Malloc(sizeof(double));
+          levels = (double *) malloc(sizeof(double));
           levels[0] = (double) atof(szc_value);
 
-          char *szc_key = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
-          sprintf(szc_key, "%s_bounds", zaxis);
+          char szc_key[CMOR_MAX_STRING];
+          std::snprintf(szc_key, CMOR_MAX_STRING, "%s_bounds", zaxis);
 
           int numchar = 0;
           std::vector<std::string> szc_bndss = kv_get_vals(kvl, szc_key, &numchar);
@@ -3067,7 +3079,7 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
                 cdo_abort("ERROR (infile: '%s')! Internal error.", cdo_get_stream_name(0));
             }
 
-          sprintf(szc_key, "%s_units", zaxis);
+          std::snprintf(szc_key, CMOR_MAX_STRING, "%s_units", zaxis);
           char *szcunits = kv_get_a_val(kvl, (const char *) szc_key, "m");
 
           if (Options::cdoVerbose)
@@ -3076,7 +3088,7 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
             cmf = cmor_axis(new_axis_id(axis_ids), zaxis, (char *) szcunits, zsize, (void *) levels, 'd', szc_bnds, 2, nullptr);
           else
             cmf = cmor_axis(new_axis_id(axis_ids), zaxis, (char *) szcunits, zsize, (void *) levels, 'd', nullptr, 0, nullptr);
-          Free(levels);
+          free(levels);
         }
       else
         cdo_print("You specified z_axis='%s'.\n          No value has been specified, axis will be created with the "
@@ -3086,7 +3098,7 @@ register_z_axis(KVList *kvl, int vlistID, int varID, int zaxisID, char *varname,
   else
     cdo_print("Vertical axis is either default and scalar or not available.");
 
-  Free(chardim);
+  free(chardim);
   if (cmf != 0) cdo_abort("ERROR (infile: '%s')! Function cmor_axis failed!", cdo_get_stream_name(0));
 }
 
@@ -3100,14 +3112,14 @@ dimension.\n");
   int varndims, varnattsp;
   int *vardimids;
 
-  char *varname = Malloc(36 * sizeof(char));
-  char *dimname = Malloc(36 * sizeof(char));
+  char *varname = malloc(36);
+  char *dimname = malloc(36);
 
   size_t dimlength, dimstrlength;
 
   nc_open(filename, NC_NOWRITE, &nc_file_id);
   nc_inq(nc_file_id, &nfiledims, &nvars, &ngatts, &unlimdimid);
-  vardimids = Malloc(nfiledims * sizeof(int));
+  vardimids = malloc(nfiledims * sizeof(int));
   void *final_chardim;
   for ( int i = 0; i < nvars; i++ )
     {
@@ -3117,15 +3129,15 @@ dimension.\n");
           nc_inq_dim(nc_file_id, vardimids[1], dimname, &dimstrlength);
           nc_inq_dim(nc_file_id, vardimids[0], dimname, &dimlength);
 
-          final_chardim = (void *)Malloc(dimstrlength * dimlength *sizeof(char));
+          final_chardim = (void *)malloc(dimstrlength * dimlength *sizeof(char));
           nc_get_var(nc_file_id, i, final_chardim);
         }
     }
   nc_close(nc_file_id);
   cmor_axis(new_axis_id(axis_ids), dimname, "", dimlength, final_chardim, 'c',  nullptr, dimstrlength, nullptr);
-  Free(varname);
-  Free(dimname);
-  Free(vardimids);
+  free(varname);
+  free(dimname);
+  free(vardimids);
 }
 */
 static void
@@ -3274,11 +3286,11 @@ get_cmor_table(KVList *kvl, char *project_id)
   char *mip_table_dir = kv_get_a_val(kvl, "mip_table_dir", nullptr);
 #if (CMOR_VERSION_MAJOR == 2)
   if (mip_table_dir[strlen(mip_table_dir) - 1] == '/')
-    sprintf(gridtable, "%s%s_grids", mip_table_dir, project_id);
+    std::snprintf(gridtable, CMOR_MAX_STRING, "%s%s_grids", mip_table_dir, project_id);
   else
-    sprintf(gridtable, "%s/%s_grids", mip_table_dir, project_id);
+    std::snprintf(gridtable, CMOR_MAX_STRING, "%s/%s_grids", mip_table_dir, project_id);
 #elif (CMOR_VERSION_MAJOR == 3)
-  sprintf(gridtable, "%s/%s_grids.json", mip_table_dir, project_id);
+  std::snprintf(gridtable, CMOR_MAX_STRING, "%s/%s_grids.json", mip_table_dir, project_id);
 #endif
   if (file_exist(gridtable, false, "Cmor-grid_table", false))
     {
@@ -3470,10 +3482,10 @@ register_projection(int *grid_ids, int projID, double *ycoord_vals, double *xcoo
   int pynbounds;
   int pylength = gridInqYsize(projID);
   int pxlength = gridInqXsize(projID);
-  double *pxcoord_vals = (double *) Malloc(pxlength * sizeof(double));
-  double *pycoord_vals = (double *) Malloc(pylength * sizeof(double));
-  double *pxcell_bounds = (double *) Malloc(2 * pxlength * sizeof(double));
-  double *pycell_bounds = (double *) Malloc(2 * pylength * sizeof(double));
+  double *pxcoord_vals = (double *) malloc(pxlength * sizeof(double));
+  double *pycoord_vals = (double *) malloc(pylength * sizeof(double));
+  double *pxcell_bounds = (double *) malloc(2 * pxlength * sizeof(double));
+  double *pycell_bounds = (double *) malloc(2 * pylength * sizeof(double));
   inquire_vals_and_bounds(projID, &pxnbounds, &pynbounds, pxcoord_vals, pycoord_vals, pxcell_bounds, pycell_bounds);
   check_and_gen_bounds(projID, pxnbounds, pxlength, pxcoord_vals, pxcell_bounds, 1);
   check_and_gen_bounds(projID, pynbounds, pylength, pycoord_vals, pycell_bounds, 0);
@@ -3622,7 +3634,7 @@ register_projection(int *grid_ids, int projID, double *ycoord_vals, double *xcoo
                 "set to 0.0 by default in case they are not given.",
                 p_len, natts);
 
-  parameter_values = (double *) Malloc(p_len * sizeof(double));
+  parameter_values = (double *) malloc(p_len * sizeof(double));
   for (int i = 0; i < p_len; ++i) parameter_values[i] = 0.0;
 
   for (int iatt = 0; iatt < natts; ++iatt)
@@ -3694,8 +3706,8 @@ register_projection(int *grid_ids, int projID, double *ycoord_vals, double *xcoo
   else if (projtype == CDI_PROJ_LCC)
     {
       memcpy(u_cmor, "      \0      \0      \0      \0", 4 * l_u_cmor);
-      double *xii = (double *) Malloc(xlength * sizeof(double));
-      double *yii = (double *) Malloc(ylength * sizeof(double));
+      double *xii = (double *) malloc(xlength * sizeof(double));
+      double *yii = (double *) malloc(ylength * sizeof(double));
       for (int i = 0; i < xlength; ++i) xii[i] = (double) i;
       for (int i = 0; i < ylength; ++i) yii[i] = (double) i;
       cmf = cmor_axis(&grid_axis[0], (char *) "x", (char *) "m", ylength, (void *) yii, 'd', 0, 0, nullptr);
@@ -3708,8 +3720,8 @@ register_projection(int *grid_ids, int projID, double *ycoord_vals, double *xcoo
 #elif (CMOR_VERSION_MAJOR == 3)
       cmf = cmor_set_grid_mapping(grid_ids[0], mapping, p_len, p_lcc_cmor, l_p_lcc, parameter_values, u_cmor, l_u_cmor);
 #endif
-      Free(xii);
-      Free(yii);
+      free(xii);
+      free(yii);
     }
   else if (projtype == CDI_PROJ_STERE)
     {
@@ -3728,11 +3740,11 @@ register_projection(int *grid_ids, int projID, double *ycoord_vals, double *xcoo
 #endif
     }
 
-  Free(parameter_values);
-  Free(pxcell_bounds);
-  Free(pycell_bounds);
-  Free(pxcoord_vals);
-  Free(pycoord_vals);
+  free(parameter_values);
+  free(pxcell_bounds);
+  free(pycell_bounds);
+  free(pxcoord_vals);
+  free(pycoord_vals);
   if (cmf != 0) cdo_abort("ERROR (infile: '%s')! Function cmor_axis or cmor_set_grid_mapping failed!", cdo_get_stream_name(0));
 }
 
@@ -3779,10 +3791,10 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
       if (type == GRID_GAUSSIAN || type == GRID_LONLAT)
         {
           grid_ids[0] = 0;
-          xcoord_vals = (double *) Malloc(xlength * sizeof(double));
-          ycoord_vals = (double *) Malloc(ylength * sizeof(double));
-          xcell_bounds = (double *) Malloc(2 * xlength * sizeof(double));
-          ycell_bounds = (double *) Malloc(2 * ylength * sizeof(double));
+          xcoord_vals = (double *) malloc(xlength * sizeof(double));
+          ycoord_vals = (double *) malloc(ylength * sizeof(double));
+          xcell_bounds = (double *) malloc(2 * xlength * sizeof(double));
+          ycell_bounds = (double *) malloc(2 * ylength * sizeof(double));
           inquire_vals_and_bounds(gridID, &xnbounds, &ynbounds, xcoord_vals, ycoord_vals, xcell_bounds, ycell_bounds);
 
           check_and_gen_bounds(gridID, xnbounds, xlength, xcoord_vals, xcell_bounds, 1);
@@ -3795,28 +3807,28 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
             cmf = cmor_axis(new_axis_id(axis_ids), (char *) "longitude", (char *) "degrees_east", xlength, (void *) xcoord_vals,
                             'd', (void *) xcell_bounds, 2, nullptr);
 
-          Free(xcell_bounds);
-          Free(ycell_bounds);
-          Free(xcoord_vals);
-          Free(ycoord_vals);
+          free(xcell_bounds);
+          free(ycell_bounds);
+          free(xcoord_vals);
+          free(ycoord_vals);
         }
       else if (type == GRID_UNSTRUCTURED)
         {
           int nvertex = gridInqNvertex(gridID);
-          xcoord_vals = (double *) Malloc(totalsize * sizeof(double));
-          ycoord_vals = (double *) Malloc(totalsize * sizeof(double));
+          xcoord_vals = (double *) malloc(totalsize * sizeof(double));
+          ycoord_vals = (double *) malloc(totalsize * sizeof(double));
           /* maximal 4 gridbounds per gridcell permitted */
           if (nvertex)
             {
-              xcell_bounds = (double *) Malloc(nvertex * totalsize * sizeof(double));
-              ycell_bounds = (double *) Malloc(nvertex * totalsize * sizeof(double));
+              xcell_bounds = (double *) malloc(nvertex * totalsize * sizeof(double));
+              ycell_bounds = (double *) malloc(nvertex * totalsize * sizeof(double));
             }
           inquire_vals_and_bounds(gridID, &xnbounds, &ynbounds, xcoord_vals, ycoord_vals, xcell_bounds, ycell_bounds);
           /* In a projection, this is done by setting mapping parameter */
           if (strcmp(movelons, "y") == 0) move_lons(xcoord_vals, xcell_bounds, totalsize, nvertex * totalsize, xnbounds);
           int grid_axis[2];
           double *coord_vals;
-          coord_vals = (double *) Malloc(xlength * sizeof(double));
+          coord_vals = (double *) malloc(xlength * sizeof(double));
           for (int j = 0; j < xlength; ++j) coord_vals[j] = (double) j;
           if (strcmp(chardim, "site") == 0)
             {
@@ -3832,19 +3844,19 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
               cmf = cmor_grid(&grid_ids[0], 1, grid_axis, 'd', (void *) ycoord_vals, (void *) xcoord_vals, nvertex,
                               (void *) ycell_bounds, (void *) xcell_bounds);
             }
-          Free(coord_vals);
-          Free(xcoord_vals);
-          Free(ycoord_vals);
-          if (xcell_bounds) Free(xcell_bounds);
-          if (ycell_bounds) Free(ycell_bounds);
+          free(coord_vals);
+          free(xcoord_vals);
+          free(ycoord_vals);
+          if (xcell_bounds) free(xcell_bounds);
+          if (ycell_bounds) free(ycell_bounds);
         }
       else if (type == GRID_CURVILINEAR)
         {
-          xcoord_vals = (double *) Malloc(totalsize * sizeof(double));
-          ycoord_vals = (double *) Malloc(totalsize * sizeof(double));
+          xcoord_vals = (double *) malloc(totalsize * sizeof(double));
+          ycoord_vals = (double *) malloc(totalsize * sizeof(double));
           /* maximal 4 gridbounds per gridcell permitted */
-          xcell_bounds = (double *) Malloc(4 * totalsize * sizeof(double));
-          ycell_bounds = (double *) Malloc(4 * totalsize * sizeof(double));
+          xcell_bounds = (double *) malloc(4 * totalsize * sizeof(double));
+          ycell_bounds = (double *) malloc(4 * totalsize * sizeof(double));
           inquire_vals_and_bounds(gridID, &xnbounds, &ynbounds, xcoord_vals, ycoord_vals, xcell_bounds, ycell_bounds);
           /* In a projection, this is done by setting mapping parameter */
           if (strcmp(movelons, "y") == 0) move_lons(xcoord_vals, xcell_bounds, totalsize, 4 * totalsize, xnbounds);
@@ -3856,20 +3868,20 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
             {
               double *xncoord_vals;
               double *yncoord_vals;
-              xncoord_vals = (double *) Malloc(xlength * sizeof(double));
-              yncoord_vals = (double *) Malloc(ylength * sizeof(double));
+              xncoord_vals = (double *) malloc(xlength * sizeof(double));
+              yncoord_vals = (double *) malloc(ylength * sizeof(double));
               for (int j = 0; j < ylength; ++j) yncoord_vals[j] = (double) j;
               for (int j = 0; j < xlength; ++j) xncoord_vals[j] = (double) j;
               cmf = cmor_axis(&grid_axis[0], (char *) "j_index", (char *) "1", ylength, (void *) yncoord_vals, 'd', 0, 0, nullptr);
               cmf = cmor_axis(&grid_axis[1], (char *) "i_index", (char *) "1", xlength, (void *) xncoord_vals, 'd', 0, 0, nullptr);
               cmf = cmor_grid(&grid_ids[0], 2, grid_axis, 'd', (void *) ycoord_vals, (void *) xcoord_vals, 4, (void *) ycell_bounds,
                               (void *) xcell_bounds);
-              Free(xncoord_vals);
-              Free(yncoord_vals);
-              Free(xcoord_vals);
-              Free(ycoord_vals);
-              Free(xcell_bounds);
-              Free(ycell_bounds);
+              free(xncoord_vals);
+              free(yncoord_vals);
+              free(xcoord_vals);
+              free(ycoord_vals);
+              free(xcell_bounds);
+              free(ycell_bounds);
             }
           /*else
             {
@@ -3890,15 +3902,15 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
           if (Options::cdoVerbose) cdo_print("Start to define a character axis '%s' instead of a grid axis'.", chardim);
           grid_ids[0] = 0;
           int numchar = 0;
-          char *charvalstring = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
-          sprintf(charvalstring, "char_axis_%s_%s", chardim, cmor_name);
+          char charvalstring[CMOR_MAX_STRING];
+          std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s_%s", chardim, cmor_name);
           std::vector<std::string> charvals = kv_get_vals(kvl, charvalstring, &numchar);
           if (numchar == 0)
             {
-              sprintf(charvalstring, "char_axis_%s", chardim);
+              std::snprintf(charvalstring, CMOR_MAX_STRING, "char_axis_%s", chardim);
               charvals = kv_get_vals(kvl, charvalstring, &numchar);
             }
-          Free(charvalstring);
+
           if ((xlength > 0 && xlength != numchar) && (ylength > 0 && ylength != numchar))
             cdo_abort(
                 "ERROR (infile: '%s')! In registration of a character coordinate as substitution for a horizontal axis:\n        "
@@ -3933,8 +3945,8 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
           if ((dimstrlen = gridInqXIsc(gridID)))
             {
               std::vector<std::string> xcharspp;
-              char **xchars = (char **) Malloc((xlength + 1) * sizeof(char *));
-              for (int i = 0; i < xlength; ++i) xchars[i] = (char *) Malloc((dimstrlen + 1) * sizeof(char));
+              char **xchars = (char **) malloc((xlength + 1) * sizeof(char *));
+              for (int i = 0; i < xlength; ++i) xchars[i] = (char *) malloc(dimstrlen + 1);
               gridInqXCvals(gridID, xchars);
               for (int j = 0; j < xlength; ++j) xchars[j][dimstrlen] = 0;
               xchars[xlength] = nullptr;
@@ -3948,8 +3960,8 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
           if ((dimstrlen = gridInqYIsc(gridID)))
             {
               std::vector<std::string> ycharspp;
-              char **ychars = (char **) Malloc((ylength + 1) * sizeof(char));
-              for (int i = 0; i < ylength; ++i) ychars[i] = (char *) Malloc((dimstrlen + 1) * sizeof(char));
+              char **ychars = (char **) malloc((ylength + 1) * sizeof(char *));
+              for (int i = 0; i < ylength; ++i) ychars[i] = (char *) malloc(dimstrlen + 1);
               gridInqYCvals(gridID, ychars);
               for (int j = 0; j < ylength; ++j) ychars[j][dimstrlen] = 0;
               ychars[ylength] = nullptr;
@@ -3975,15 +3987,15 @@ register_grid(KVList *kvl, int vlistID, int varID, int *axis_ids, int *grid_ids,
       if (projtype != CDI_UNDEFID)
         {
           register_projection(grid_ids, projID, ycoord_vals, xcoord_vals, ycell_bounds, xcell_bounds, xlength, ylength, projtype);
-          Free(xcoord_vals);
-          Free(ycoord_vals);
-          Free(xcell_bounds);
-          Free(ycell_bounds);
+          free(xcoord_vals);
+          free(ycoord_vals);
+          free(xcell_bounds);
+          free(ycell_bounds);
         }
     }
   else
     grid_ids[0] = 0;
-  Free(chardim);
+  free(chardim);
   if (cmf != 0) cdo_abort("ERROR (infile: '%s')! Function cmor_axis failed!", cdo_get_stream_name(0));
 }
 
@@ -3996,7 +4008,7 @@ register_variable(KVList *kvl, int vlistID, int varID, int *axis_ids, struct map
   char *origname = get_txtatt(vlistID, varID, "original_name");
   char *history = get_txtatt(vlistID, varID, "history");
   char *varcom = get_txtatt(vlistID, varID, "variable_comment");
-  char *units = (char *) Malloc(CDI_MAX_NAME * sizeof(char));
+  char *units = (char *) malloc(CDI_MAX_NAME);
   vlistInqVarUnits(vlistID, varID, units);
   char *attunits = kv_get_a_val(kvl, "u", nullptr);
   char *attp = kv_get_a_val(kvl, "p", nullptr);
@@ -4008,13 +4020,13 @@ register_variable(KVList *kvl, int vlistID, int varID, int *axis_ids, struct map
   check_compare_set(&origname, attorigname, "original_name", "");
   if (strcmp(origname, "") == 0 || strstr(origname, "var"))
     {
-      Free(origname);
+      free(origname);
       origname = nullptr;
     }
   check_compare_set(&varcom, attvarcom, "variable_comment", "");
   if (strcmp(varcom, "") == 0)
     {
-      Free(varcom);
+      free(varcom);
       varcom = nullptr;
     }
   if (Options::cdoVerbose)
@@ -4033,7 +4045,7 @@ register_variable(KVList *kvl, int vlistID, int varID, int *axis_ids, struct map
         {
           var->charvars = 0;
           var->datatype = 'd';
-          var->data = Malloc(gridsize * zsize * sizeof(double));
+          var->data = malloc(gridsize * zsize * sizeof(double));
         }
       *(double *) missing_value = vlistInqVarMissval(vlistID, varID);
     }
@@ -4043,7 +4055,7 @@ register_variable(KVList *kvl, int vlistID, int varID, int *axis_ids, struct map
         {
           var->charvars = 0;
           var->datatype = 'f';
-          var->data = Malloc(gridsize * zsize * sizeof(float));
+          var->data = malloc(gridsize * zsize * sizeof(float));
         }
       *(float *) missing_value = vlistInqVarMissval(vlistID, varID);
     }
@@ -4083,11 +4095,11 @@ register_variable(KVList *kvl, int vlistID, int varID, int *axis_ids, struct map
       if (Options::cdoVerbose) cdo_print("8.5.3. Successfully set deflate for variable '%s'.", name);
     }
 #endif
-  if (positive) Free(positive);
-  if (origname) Free(origname);
-  if (history) Free(history);
-  if (units) Free(units);
-  if (varcom) Free(varcom);
+  if (positive) free(positive);
+  if (origname) free(origname);
+  if (history) free(history);
+  if (units) free(units);
+  if (varcom) free(varcom);
 }
 
 static void
@@ -4314,13 +4326,13 @@ register_all_dimensions(KVList *kvl, CdoStreamID streamID, struct mapping vars[]
               if (vlistInqVarDatatype(vlistID, mergeIDs[mergeID]) == CDI_DATATYPE_FLT64)
                 {
                   var->datatype = 'd';
-                  var->data = Malloc(gridInqSize(vlistInqVarGrid(vlistID, mergeIDs[mergeID]))
+                  var->data = malloc(gridInqSize(vlistInqVarGrid(vlistID, mergeIDs[mergeID]))
                                      * zaxisInqSize(vlistInqVarZaxis(vlistID, mergeIDs[mergeID])) * sizeof(double));
                 }
               else
                 {
                   var->datatype = 'f';
-                  var->data = Malloc(gridInqSize(vlistInqVarGrid(vlistID, mergeIDs[mergeID]))
+                  var->data = malloc(gridInqSize(vlistInqVarGrid(vlistID, mergeIDs[mergeID]))
                                      * zaxisInqSize(vlistInqVarZaxis(vlistID, mergeIDs[mergeID])) * sizeof(float));
                 }
             }
@@ -4347,7 +4359,7 @@ register_all_dimensions(KVList *kvl, CdoStreamID streamID, struct mapping vars[]
 static char *
 get_frequency(/*KVList *kvl,*/ int vlistID, int miptab_freq)
 {
-  char *frequency = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
+  char *frequency = (char *) malloc(CMOR_MAX_STRING);
   std::strcpy(frequency, "no");
   int ntsteps = vlistNtsteps(vlistID);
   int reccounter = 0;
@@ -4668,7 +4680,7 @@ get_time_bounds(KVList *kvl, int taxisID, int ifreq, JulianDate ref_date, Julian
                 double *time_bnds, int time_axis, int /*vlistID*/)
 {
   double time_val = julianDate_to_seconds(julianDate_sub(jtime_val, ref_date)) / tunitsec;
-  const auto vDateTime = taxisInqVdatetime(taxisID);
+  auto vDateTime = taxisInqVdatetime(taxisID);
   auto vDateTimeCorr = vDateTime;
   CdiDateTime vDateTime0b{};
   CdiDateTime vDateTime1b{};
@@ -4905,7 +4917,7 @@ read_record(CdoStreamID streamID, struct mapping vars[], int vlistID)
   int gridID = vlistInqVarGrid(vlistID, varID);
   int type = gridInqType(gridID);
   auto gridsize = gridInqSize(gridID);
-  double *buffer = (double *) Malloc(gridsize * sizeof(double));
+  double *buffer = (double *) malloc(gridsize * sizeof(double));
 
   struct mapping *var = map_var(varID, vars);
   if (var && var->charvars != 1)
@@ -4914,8 +4926,8 @@ read_record(CdoStreamID streamID, struct mapping vars[], int vlistID)
       int ztype = zaxisInqType(zaxisID);
       /*      int latdim = gridInqYsize(gridID); */
       int levdim = zaxisInqSize(zaxisID);
-      size_t nmiss;
-      cdo_read_record(streamID, buffer, &nmiss);
+      size_t numMissVals;
+      cdo_read_record(streamID, buffer, &numMissVals);
       for (size_t i = 0; i < gridsize; ++i)
         {
           // Wrong:  (lat x basin, lev ) gridsize * levelID + i
@@ -4942,7 +4954,7 @@ read_record(CdoStreamID streamID, struct mapping vars[], int vlistID)
           else { ((double *) var->data)[newIndex] = (double) buffer[i]; }
         }
     }
-  Free(buffer);
+  free(buffer);
 }
 
 static int
@@ -5064,7 +5076,7 @@ check_append_and_size(KVList *kvl, int /*vlistID*/, char *testIn, int ifreq, int
   CdiStreamID streamID2 = streamOpenRead(cdo_get_stream_name(0));
   int vlistID2 = streamInqVlist(streamID2);
   int taxisID2 = vlistInqTaxis(vlistID2);
-  const auto vDateTime2 = taxisInqVdatetime(taxisID2);
+  auto vDateTime2 = taxisInqVdatetime(taxisID2);
   auto firstdate = julianDate_encode(calendar, vDateTime2);
 
   int fyear, fmonth, dummy;
@@ -5176,7 +5188,7 @@ check_append_and_size(KVList *kvl, int /*vlistID*/, char *testIn, int ifreq, int
 static char *
 use_chunk_des_files(KVList *kvl, int vlistID, int /*var_id*/, char *chunk_des_file, int ifreq, int calendar)
 {
-  char *chunk_file = (char *) Malloc(4096 * sizeof(char));
+  char *chunk_file = (char *) malloc(4096);
   if (file_exist(chunk_des_file, false, "chunk_description", false))
     {
       auto *fp = std::fopen(chunk_des_file, "r");
@@ -5248,14 +5260,14 @@ static char **
 get_chunk_des_files(KVList *kvl, struct mapping vars[], char *miptab_freqptr, int nreq, int vlistID, char *charname,
                     char *project_id)
 {
-  char **chunk_des_files = (char **) Malloc((nreq + 1) * sizeof(char *));
+  char **chunk_des_files = (char **) malloc((nreq + 1) * sizeof(char *));
   chunk_des_files[nreq] = nullptr;
 
   char trunk[CMOR_MAX_STRING];
   if (strcmp(project_id, "CMIP6") == 0)
-    sprintf(trunk, "%s_", kv_get_a_val(kvl, "source_id", ""));
+    std::snprintf(trunk, CMOR_MAX_STRING, "%s_", kv_get_a_val(kvl, "source_id", ""));
   else
-    sprintf(trunk, "%s_", kv_get_a_val(kvl, "model_id", ""));
+    std::snprintf(trunk, CMOR_MAX_STRING, "%s_", kv_get_a_val(kvl, "model_id", ""));
   const char *description_atts[] = { "experiment_id", "member", "sub_experiment_id", nullptr };
   std::strcpy(trunk, miptab_freqptr);
   for (int i = 0; description_atts[i]; ++i)
@@ -5266,14 +5278,13 @@ get_chunk_des_files(KVList *kvl, struct mapping vars[], char *miptab_freqptr, in
 
   for (int j = 0; vars[j].cmor_varID != CMOR_UNDEFID; ++j)
     {
-      char *name = (char *) Malloc(CDI_MAX_NAME * sizeof(char));
+      char name[CDI_MAX_NAME];
       if (charname)
         std::strcpy(name, charname);
       else
         vlistInqVarName(vlistID, vars[j].cdi_varID, name);
-      chunk_des_files[j] = (char *) Malloc(CMOR_MAX_STRING * sizeof(char));
-      sprintf(chunk_des_files[j], ".CHUNK_FILE_%s_%s.txt", name, trunk);
-      Free(name);
+      chunk_des_files[j] = (char *) malloc(CMOR_MAX_STRING);
+      std::snprintf(chunk_des_files[j], CMOR_MAX_STRING, ".CHUNK_FILE_%s_%s.txt", name, trunk);
     }
   return chunk_des_files;
 }
@@ -5286,7 +5297,7 @@ get_chunk_files(KVList *kvl, struct mapping vars[], int vlistID, int ifreq, int
   for (i = 0; vars[i].cmor_varID != CMOR_UNDEFID; ++i)
     ;
   if (mergeIDs[0] != CMOR_UNDEFID) i = 1;
-  char **chunk_files = (char **) Malloc((i + 1) * sizeof(char *));
+  char **chunk_files = (char **) malloc((i + 1) * sizeof(char *));
   chunk_files[i] = nullptr;
 
   if (Options::cdoVerbose) cdo_print("10.2.1. Start to validate append mode.");
@@ -5380,9 +5391,9 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
   if (Options::cdoVerbose) cdo_print("10. Start to write variables via cmor_write.");
   if (Options::cdoVerbose) cdo_print("10.1. Start to get frequency.");
   int time_unit;
-  const auto sDateTime = get_taxis(kv_get_a_val(kvl, "rtu", nullptr), &time_unit);
+  auto sDateTime = get_taxis(kv_get_a_val(kvl, "rtu", nullptr), &time_unit);
   int tunitsec = get_tunitsec(time_unit);
-  const auto ref_date = julianDate_encode(calendar, sDateTime);
+  auto ref_date = julianDate_encode(calendar, sDateTime);
   char *frequency = nullptr;
   if (time_axis != 4)
     {
@@ -5420,7 +5431,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
     {
       int number = 0;
       for (number = 0; vars[number].cmor_varID != CMOR_UNDEFID; number++)
-        chunk_files = (char **) Malloc((number + 1) * sizeof(char *));
+        chunk_files = (char **) malloc((number + 1) * sizeof(char *));
       empty_array(vars, &chunk_files);
     }
   if (ifreq == 7) cdo_print("10.2. Append mode not possible for frequency '%s'. Switch to replace mode.", frequency);
@@ -5431,7 +5442,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
       while (chunk_files[i])
         {
           char command[CDI_MAX_NAME];
-          sprintf(command, "cp %s %s.save", chunk_files[i], chunk_files[i]);
+          std::snprintf(command, CDI_MAX_NAME, "cp %s %s.save", chunk_files[i], chunk_files[i]);
           int dir_err = system(command);
           if (dir_err != 0) cdo_warning("Could not create a .save file out of the previous chunk '%s'.", chunk_files[i]);
           i++;
@@ -5448,7 +5459,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
         if (Options::cdoVerbose) cdo_print("10.3. Start to get auxiliary variables.");
         zaxisID = vlistInqVarZaxis(vlistID, vars[i].cdi_varID);
         zsize = zaxisInqSize(zaxisID);
-        charname = (char *) Malloc(CDI_MAX_NAME * sizeof(char));
+        charname = (char *) malloc(CDI_MAX_NAME * sizeof(char));
         vlistInqVarName(vlistID, vars[i].cdi_varID, charname);
 
         cdo_stream_close(streamID);
@@ -5472,12 +5483,11 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
   if (mergeIDs[0] != CMOR_UNDEFID)
     {
       while (mergeIDs[fsize] != CMOR_UNDEFID) fsize++;
-      ;
       if (Options::cdoVerbose) cdo_print("10.3. '%d' Variables will be merged.", fsize);
       zaxisID = vlistInqVarZaxis(vlistID, mergeIDs[0]);
       zsize = zaxisInqSize(zaxisID);
 
-      mergeIdx = (int *) Malloc(fsize * sizeof(int));
+      mergeIdx = (int *) malloc(fsize * sizeof(int));
       mergeIdx[0] = -1;
       for (int j = 0; j < fsize; j++)
         {
@@ -5509,9 +5519,9 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
         {
           void *dataslice;
           if (vars[mergeIdx[0]].datatype == 'd')
-            dataslice = (void *) Malloc(gridsize * zsize * fsize * sizeof(double));
+            dataslice = (void *) malloc(gridsize * zsize * fsize * sizeof(double));
           else
-            dataslice = (void *) Malloc(gridsize * zsize * fsize * sizeof(float));
+            dataslice = (void *) malloc(gridsize * zsize * fsize * sizeof(float));
 
           for (i = 0; i < fsize; i++)
             {
@@ -5539,7 +5549,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
           cmf = cmor_write(vars[mergeIdx[0]].cmor_varID, dataslice, vars[mergeIdx[0]].datatype, chunk_files[0], 1, &time_val,
                            time_bndsp, NULL);
 #endif
-          Free(dataslice);
+          free(dataslice);
         }
       for (i = 0; vars[i].cmor_varID != CMOR_UNDEFID; ++i)
         {
@@ -5554,13 +5564,13 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                       void *dataslice;
                       if (vars[i].datatype == 'd')
                         {
-                          dataslice = (void *) Malloc(gridsize * zsize * sizeof(double));
+                          dataslice = (void *) malloc(gridsize * zsize * sizeof(double));
                           for (int j = 0; j < (int) gridsize * zsize; ++j)
                             ((double *) dataslice)[j] = ((double *) vars[i].data)[(tsID - 1) * gridsize * zsize + j];
                         }
                       else
                         {
-                          dataslice = (void *) Malloc(gridsize * zsize * sizeof(float));
+                          dataslice = (void *) malloc(gridsize * zsize * sizeof(float));
                           for (int j = 0; j < (int) gridsize * zsize; ++j)
                             ((float *) dataslice)[j] = ((float *) vars[i].data)[(tsID - 1) * gridsize * zsize + j];
                         }
@@ -5570,7 +5580,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                       cmf = cmor_write(vars[i].cmor_varID, dataslice, vars[i].datatype, chunk_files[i], 1, &time_val, time_bndsp,
                                        nullptr);
 #endif
-                      Free(dataslice);
+                      free(dataslice);
                     }
                   else if (vars[i].cdi_varID != mergeIDs[0])
                     {
@@ -5634,16 +5644,16 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                   status = nc_get_att_text(ncid, NC_GLOBAL, "tracking_id", prelim);
                   char *prefixCordex = strdup("hdl:21.14103/");
                   int lengthCombi = strlen(prelim) + strlen(prefixCordex);
-                  char *track = (char *) Malloc(lengthCombi * sizeof(char));
-                  sprintf(track, "%s%s", prefixCordex, prelim);
+                  char *track = (char *) malloc(lengthCombi);
+                  std::snprintf(track, lengthCombi, "%s%s", prefixCordex, prelim);
                   status = nc_put_att_text(ncid, NC_GLOBAL, "tracking_id", (size_t) lengthCombi, (const char *) track);
                   status = nc_enddef(ncid);
                   status = nc_close(ncid);
                   if (status != NC_NOERR)
                     cdo_abort("ERROR (infile: '%s')! Could not set a prefix for tracking_id", cdo_get_stream_name(0));
                   if (Options::cdoVerbose) cdo_print("11.1. Successfully set a prefix for tracking_id.");
-                  Free(track);
-                  Free(prefixCordex);
+                  free(track);
+                  free(prefixCordex);
                 }
             }
 #endif
@@ -5656,7 +5666,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                             char *prelim = get_txtatt(vlistIDF, CDI_GLOBAL, "tracking_id");
                             char *prefixCordex = strdup("21.14103/");
                             size_t lengthCombi = (size_t) (strlen(prelim) + strlen(prefixCordex));
-                            char *track = (char *) Malloc( lengthCombi *sizeof(char));
+                            char *track = (char *) malloc( lengthCombi *sizeof(char));
                             sprintf(track, "%s%s", prefixCordex, prelim);
                             cdiDefAttTxt(vlistIDF, CDI_GLOBAL, "tracking_id", lengthCombi, (const char *)track);
                             streamClose(streamIDF);
@@ -5721,52 +5731,52 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                   smon1[2] = '\0';
                   smon2[2] = '\0';
                   if (atol(smon1) != 1)
-                    sprintf(smon1, "%02ld", atol(smon1) - 1);
+                    std::snprintf(smon1, sizeof(smon1), "%02ld", atol(smon1) - 1);
                   else
                     {
                       char syr[5];
                       strncpy(syr, &timename[1], 4);
                       syr[4] = '\0';
-                      sprintf(syr, "%04ld", atol(syr) - 1);
+                      std::snprintf(syr, sizeof(syr), "%04ld", atol(syr) - 1);
                       strncpy(&timename[1], syr, 4);
 
-                      sprintf(smon1, "12");
+                      std::snprintf(smon1, sizeof(smon1), "12");
                     }
                   if (atol(smon2) != 1)
-                    sprintf(smon2, "%02ld", atol(smon2) + 1);
+                    std::snprintf(smon2, sizeof(smon2), "%02ld", atol(smon2) + 1);
                   else
                     {
                       char syr[12];
                       strncpy(syr, &timename[8], 4);
                       syr[4] = '\0';
-                      sprintf(syr, "%04ld", atol(syr) - 1);
+                      std::snprintf(syr, sizeof(syr), "%04ld", atol(syr) - 1);
                       strncpy(&timename[8], syr, 4);
 
-                      sprintf(smon2, "12");
+                      std::snprintf(smon2, sizeof(smon2), "12");
                     }
                   strncpy(&timename[5], smon1, 2);
                   strncpy(&timename[12], smon2, 2);
                 }
 
               int cmdlen = 11 + strlen(kv_get_a_val(kvl, "cordexDir", nullptr)) + strlen(varname);
-              char command1[cmdlen];
-              std::snprintf(command1, cmdlen, "mkdir -p %s/%s", kv_get_a_val(kvl, "cordexDir", nullptr), varname);
+              std::vector<char> command1(cmdlen);
+              std::snprintf(command1.data(), cmdlen, "mkdir -p %s/%s", kv_get_a_val(kvl, "cordexDir", nullptr), varname);
 
-              int dir_err = system(command1);
+              int dir_err = system(command1.data());
               if (dir_err != 0)
                 {
                   cdo_warning("Could not create CORDEX compliant path for output files of cdo cmor. Files are created "
                               "in current working directory.");
                 }
 
-              sprintf(cordex_file_name, "%s/%s/%s_%s%s", kv_get_a_val(kvl, "cordexDir", nullptr), varname, varname,
-                      kv_get_a_val(kvl, "cordexFileTem", nullptr), timename);
+              std::snprintf(cordex_file_name, CMOR_MAX_STRING, "%s/%s/%s_%s%s", kv_get_a_val(kvl, "cordexDir", nullptr), varname,
+                            varname, kv_get_a_val(kvl, "cordexFileTem", nullptr), timename);
 
               cmdlen = 5 + strlen(file_name) + strlen(cordex_file_name);
-              char command2[cmdlen];
+              std::vector<char> command2(cmdlen);
 
-              sprintf(command2, "mv %s %s", file_name, cordex_file_name);
-              dir_err = system(command2);
+              std::snprintf(command2.data(), cmdlen, "mv %s %s", file_name, cordex_file_name);
+              dir_err = system(command2.data());
               if (dir_err != 0)
                 {
                   cdo_warning("Could not move cdo cmor output file to CORDEX compliant path.");
@@ -5789,8 +5799,8 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                 {
                   char newname[CDI_MAX_NAME], oldmember[CDI_MAX_NAME], newmember[CDI_MAX_NAME], chunkpath[CDI_MAX_NAME],
                       oldchunkpath[CDI_MAX_NAME];
-                  sprintf(oldmember, "r%ldi", atol(realization));
-                  sprintf(newmember, "r%si", realization);
+                  std::snprintf(oldmember, CDI_MAX_NAME, "r%ldi", atol(realization));
+                  std::snprintf(newmember, CDI_MAX_NAME, "r%si", realization);
 
                   char *startcmp = file_name;
                   int startpattern = 0, lastSlash = 0;
@@ -5808,10 +5818,10 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                             chunkpath[startpattern + strlen(newmember) - patternlength] = '\0';
                           startcmp += patternlength;
                           startpattern += patternlength;
-                          sprintf(newname, "%s%s%s", chunkpath, newmember, startcmp);
+                          std::snprintf(newname, CDI_MAX_NAME, "%s%s%s", chunkpath, newmember, startcmp);
                           if (!oldchunkcopied)
                             {
-                              sprintf(oldchunkpath, "%s%s", chunkpath, oldmember);
+                              std::snprintf(oldchunkpath, CDI_MAX_NAME, "%s%s", chunkpath, oldmember);
                               oldchunkcopied = true;
                             }
                           std::strcpy(chunkpath, newname);
@@ -5829,7 +5839,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                   std::strcpy(chunkpath, newname);
                   chunkpath[lastSlash] = '\0';
                   char command[CDI_MAX_NAME];
-                  sprintf(command, "mkdir -p %s; mv %s %s;", chunkpath, file_name, newname);
+                  std::snprintf(command, CDI_MAX_NAME, "mkdir -p %s; mv %s %s;", chunkpath, file_name, newname);
 
                   int dir_err = system(command);
                   if (dir_err != 0)
@@ -5844,7 +5854,7 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
                       if (Options::silentMode) cdo_warning("     File stored in:  '%s' with cmor!", newname);
                     }
 
-                  sprintf(command, "rmdir %s*;", oldchunkpath);
+                  std::snprintf(command, CDI_MAX_NAME, "rmdir %s*;", oldchunkpath);
                   if (Options::cdoVerbose) cdo_print("Start to remove wrong data path (r1 instead of r0*).");
                   dir_err = system(command);
                   if (dir_err != 0)
@@ -5878,11 +5888,11 @@ write_variables(KVList *kvl, CdoStreamID streamID, struct mapping vars[], int mi
         }
     }
   if (cmf != 0) cdo_abort("ERROR (infile: '%s')! Function cmor_close_variable failed!", cdo_get_stream_name(0));
-  if (mergeIDs[0] != CMOR_UNDEFID) Free(mergeIdx);
-  if (frequency) Free(frequency);
+  if (mergeIDs[0] != CMOR_UNDEFID) free(mergeIdx);
+  if (frequency) free(frequency);
   if (chunk_files) free_array(chunk_files);
   if (chunkdf) free_array(chunkdf);
-  if (charname) Free(charname);
+  if (charname) free(charname);
   cdo_stream_close(newstreamID);
   if (Options::cdoVerbose) cdo_print("11. Successfully closed files and freed allocated memory.");
 }
@@ -5929,8 +5939,9 @@ read_maptab(KVList *kvl, CdoStreamID streamID, char *miptabfreq, struct mapping
   if (maptab && maptabdir)
     if (maptab[0] != '/')
       {
-        maptabbuild = (char *) Malloc((strlen(maptab) + strlen(maptabdir) + 2) * sizeof(char));
-        sprintf(maptabbuild, "%s/%s", maptabdir, maptab);
+        size_t len = (strlen(maptab) + strlen(maptabdir) + 2);
+        maptabbuild = (char *) malloc(len);
+        std::snprintf(maptabbuild, len, "%s/%s", maptabdir, maptab);
       }
   if (maptab)
     {
@@ -6038,16 +6049,16 @@ read_maptab(KVList *kvl, CdoStreamID streamID, char *miptabfreq, struct mapping
       /***/
       kv_insert_vals(kvl, "mtproof", maptab, true, false);
       cdo_print("Mapping Table = '%s'.", maptab);
-      if (maptabbuild) Free(maptabbuild);
+      if (maptabbuild) free(maptabbuild);
     }
   else if (Options::cdoVerbose)
     cdo_print("5. No mapping table found.");
 }
 
 static void
-replace_key(KVList *kvl, KeyValues kv, const char *newkey)
+replace_key(KVList *kvl, const KeyValues &kv, const char *newkey)
 {
-  char **values = (char **) Malloc((kv.nvalues + 1) * sizeof(char *));
+  char **values = (char **) malloc((kv.nvalues + 1) * sizeof(char *));
   int k = 0;
   for (k = 0; k < kv.nvalues; k++) values[k] = strdup(kv.values[k].c_str());
   values[kv.nvalues] = nullptr;
@@ -6057,10 +6068,11 @@ replace_key(KVList *kvl, KeyValues kv, const char *newkey)
 }
 
 static void
-parse_cmdline(KVList *kvl, std::vector<std::string> &params, int nparams, const char *ventry)
+parse_cmdline(KVList *kvl, std::vector<std::string> &params, const char *ventry)
 {
+  (void) ventry;
   /* Already set params++ in main function */
-  if (kvl->parse_arguments(nparams - 1, params) != 0)
+  if (kvl->parse_arguments(params) != 0)
     cdo_abort("ERROR (infile: '%s')! Could not parse command line.", cdo_get_stream_name(0));
 
   std::vector<KeyValues> keystorm, keystosubs;
@@ -6087,7 +6099,7 @@ parse_cmdline(KVList *kvl, std::vector<std::string> &params, int nparams, const
 static char *
 get_mip_table(char *params, KVList *kvl, char *project_id, bool print)
 {
-  char *miptab;
+  char *miptab = nullptr;
   if (print && Options::cdoVerbose) cdo_print("2.2. Start to find a MIP table file.");
   if (!params) cdo_abort("ERROR (infile: '%s')! First parameter not passed. A MIP table file is required.", cdo_get_stream_name(0));
   if (file_exist(params, false, "MIP table", print))
@@ -6132,13 +6144,15 @@ get_mip_table(char *params, KVList *kvl, char *project_id, bool print)
         {
 #if (CMOR_VERSION_MAJOR == 2)
           {
-            miptab = (char *) Malloc((strlen(miptabdir) + strlen(project_id) + strlen(params) + 3) * sizeof(char));
-            sprintf(miptab, "%s/%s_%s", miptabdir, project_id, params);
+            size_t len = (strlen(miptabdir) + strlen(project_id) + strlen(params) + 3);
+            miptab = (char *) malloc(len);
+            std::snprintf(miptab, len, "%s/%s_%s", miptabdir, project_id, params);
           }
 #elif (CMOR_VERSION_MAJOR == 3)
           {
-            miptab = (char *) Malloc((strlen(miptabdir) + strlen(project_id) + strlen(params) + 8) * sizeof(char));
-            sprintf(miptab, "%s/%s_%s.json", miptabdir, project_id, params);
+            size_t len = (strlen(miptabdir) + strlen(project_id) + strlen(params) + 8);
+            miptab = (char *) malloc(len);
+            std::snprintf(miptab, len, "%s/%s_%s.json", miptabdir, project_id, params);
           }
 #endif
           file_exist(miptab, true, "MIP table", print);
@@ -6316,7 +6330,7 @@ cmor_load_and_set_table(KVList *kvl, char *param0, char *project_id, char **mip_
 {
   int table_id = 0, cmf = 0;
 #if (CMOR_VERSION_MAJOR == 3)
-  Free(*mip_table);
+  free(*mip_table);
   *mip_table = get_mip_table(param0, kvl, project_id, false);
 #endif
   cmf = cmor_load_table(*mip_table, &table_id);
@@ -6328,8 +6342,19 @@ cmor_load_and_set_table(KVList *kvl, char *param0, char *project_id, char **mip_
 
 #endif
 
-class ModuleCMOR
+class CMOR : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "CMOR",
+    .operators = { { "cmor", CMORHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<CMOR> registration = RegisterEntry<CMOR>(module);
 
 #ifdef HAVE_LIBCMOR
   CdoStreamID streamID;
@@ -6338,22 +6363,19 @@ class ModuleCMOR
 
   int miptab_freq;
 
-  char *project_id;
-  char *miptab_freqptr;
-  char *miptableInput;
-  char *mip_table;
+  char *project_id = nullptr;
+  char *miptab_freqptr = nullptr;
+  char *miptableInput = nullptr;
+  char *mip_table = nullptr;
 
-  struct mapping *vars;
+  struct mapping *vars = nullptr;
 #endif
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
 #ifndef HAVE_LIBCMOR
-
     cdo_abort("CMOR support not compiled in!");
 #else
     int nparams = cdo_operator_argc();
@@ -6367,7 +6389,7 @@ public:
 
     /* Define cmdline list and read cmdline */
     const char *pmlistHelper[] = { "cmdline" };
-    parse_cmdline(&kvl, params, nparams, pmlistHelper[0]);
+    parse_cmdline(&kvl, params, pmlistHelper[0]);
 
     /* Check whether a command line mapping is active */
     check_cmdline_mapping(&kvl);
@@ -6384,7 +6406,7 @@ public:
     std::vector<char> miptemp(lenmt);
     strncpy(miptemp.data(), mip_table, lenmt);
     miptemp[strlen(mip_table) - 5] = '\0';
-    Free(mip_table);
+    free(mip_table);
     mip_table = strdup(miptemp.data());
 #endif
     miptab_freq = get_miptab_freq(mip_table, project_id);
@@ -6402,6 +6424,7 @@ public:
     if (Options::cdoVerbose) cdo_print("3. Successfully opened infile '%s'.", cdo_get_stream_name(0));
 #endif
   }
+
   void
   run()
   {
@@ -6437,30 +6460,20 @@ public:
     write_variables(&kvl, streamID, vars, miptab_freq, time_axis, calendar, miptab_freqptr, project_id, mergeIDs);
 #endif
   }
+
   void
   close()
   {
 #ifdef HAVE_LIBCMOR
     destruct_var_mapping(vars);
-    Free(mip_table);
-    Free(project_id);
-    Free(miptab_freqptr);
-    /* Free(miptableInput); */
-    cdo_finish();
+    free(mip_table);
+    free(project_id);
+    free(miptab_freqptr);
+    /* free(miptableInput); */
 #endif
   }
 };
 
-void *
-CMOR(void *process)
-{
-  ModuleCMOR cmor;
-  cmor.init(process);
-  cmor.run();
-  cmor.close();
-
-  return 0;
-}
 /*
  * Local Variables:
  * c-file-style: "Java"
diff --git a/src/CMOR_lite.cc b/src/CMOR_lite.cc
index 9bb7f15275c910fd6c7795ad17523abe958cb6d7..e2e2e5bea71ce3dd6316f59697212707613a4e56 100644
--- a/src/CMOR_lite.cc
+++ b/src/CMOR_lite.cc
@@ -148,8 +148,8 @@ cmor_check_prep(CmorVar &var, long gridsize, double missval, const double *const
 static void
 apply_cmor_list(PMList &pmlist, int nvars, int vlistID2, std::vector<CmorVar> &vars)
 {
-  const std::vector<std::string> hentry = { "Header" };
-  const std::vector<std::string> ventry = { "variable_entry", "parameter" };
+  static const std::vector<std::string> hentry = { "Header" };
+  static const std::vector<std::string> ventry = { "variable_entry", "parameter" };
 
   // search for global missing value
   auto hasMissvals = false;
@@ -285,8 +285,20 @@ apply_cmor_list(PMList &pmlist, int nvars, int vlistID2, std::vector<CmorVar> &v
     }
 }
 
-class ModuleCMOR_lite
+class CMOR_lite : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "CMOR_lite",
+    .operators = { { "cmorlite", 0, 0, "parameter table name", CMORliteHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<CMOR_lite>(module);
+
   bool deleteVars = false;
   Varray<double> array;
 
@@ -304,15 +316,12 @@ class ModuleCMOR_lite
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     Options::CMOR_Mode = 1;
     if (Options::CMOR_Mode) cdiDefGlobal("CMOR_MODE", Options::CMOR_Mode);
 
-    cdo_operator_add("cmorlite", 0, 0, "parameter table name");
-
     auto operatorID = cdo_operator_id();
 
     operator_input_arg(cdo_operator_enter(operatorID));
@@ -408,6 +417,7 @@ public:
     if (vlistNumber(vlistID1) != CDI_REAL) gridsizemax *= 2;
     array = Varray<double>(gridsizemax);
   }
+
   void
   run()
   {
@@ -445,14 +455,14 @@ public:
 
             cdo_def_record(streamID2, varID2, levelID2);
 
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             auto missval = varList2[varID2].missval;
             auto gridsize = varList2[varID2].gridsize;
             if (varList2[varID2].nwpv != CDI_REAL) gridsize *= 2;
 
-            if (nmiss && var.changemissval)
+            if (numMissVals && var.changemissval)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
@@ -489,7 +499,7 @@ public:
               }
 #endif
 
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_write_record(streamID2, array.data(), numMissVals);
 
             cmor_check_prep(var, gridsize, missval, array.data());
           }
@@ -499,6 +509,7 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
@@ -511,17 +522,5 @@ public:
 
     cdo_convert_destroy();
 #endif
-
-    cdo_finish();
   }
 };
-
-void *
-CMOR_lite(void *process)
-{
-  ModuleCMOR_lite lite;
-  lite.init(process);
-  lite.run();
-  lite.close();
-  return 0;
-}
diff --git a/src/CMOR_table.cc b/src/CMOR_table.cc
index 17fcce056cfb6235048ded1475122c1a8882400d..30f4d9f9dbe4da3e573824c7e24376e549ef5997 100644
--- a/src/CMOR_table.cc
+++ b/src/CMOR_table.cc
@@ -106,21 +106,31 @@ conv_cmor_table(const PMList &pmlist)
     }
 }
 
-class ModuleCMOR_table
+class CMOR_table : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "CMOR_table",
+    .operators = { { "dump_cmor_table"}, { "conv_cmor_table"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 0, NoRestriction },
+  };
+  inline static RegisterEntry<CMOR_table> registration = RegisterEntry<CMOR_table>(module);
+  int DUMP_CMOR_TABLE, CONV_CMOR_TABLE;
   FILE *fp;
   const char *filename;
   int operatorID;
-  int DUMP_CMOR_TABLE, CONV_CMOR_TABLE;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    DUMP_CMOR_TABLE = cdo_operator_add("dump_cmor_table", 0, 0, nullptr);
-    CONV_CMOR_TABLE = cdo_operator_add("conv_cmor_table", 0, 0, nullptr);
+    DUMP_CMOR_TABLE = module.get_id("dump_cmor_table");
+    CONV_CMOR_TABLE = module.get_id("conv_cmor_table");
 
     operatorID = cdo_operator_id();
 
@@ -147,16 +157,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-CMOR_table(void *process)
-{
-  ModuleCMOR_table cmor_table;
-  cmor_table.init(process);
-  cmor_table.run();
-  cmor_table.close();
-  return nullptr;
-}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0385ad05cab6b6913587002f305c0e57ad493dae
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,537 @@
+list( APPEND cdolib_src_files
+	         	        after_dvtrans.cc        
+				after_fctrans.cc        
+				after_namelist.cc       
+				after_sptrans.cc        
+				afterburner.h           
+				afterburnerlib.cc       
+				arithmetic.h            
+				bitinformation.cc       
+				bitinformation.h        
+				cdi_lockedIO.cc
+				cdi_lockedIO.h 
+				cdi_uuid.h            
+				cdoStream.cc          
+				cdoStream.h           
+				cdo_cdi_wrapper.cc  
+				cdo_cdi_wrapper.h   
+				cdo_cmor.h 
+				cdo_default_values.cc
+				cdo_default_values.h 
+				cdo_features.cc 
+				cdo_features.h  
+				cdo_fctrans.cc           
+				cdo_fctrans.h	          
+				cdo_fft.cc                
+				cdo_fft.h                 
+				cdo_fftw3.cc                
+				cdo_fftw3.h                 
+				cdo_fill.cc               
+				cdo_fill.h               
+				cdo_getopt.cc             
+				cdo_getopt.h              
+				cdo_history.cc            
+				cdo_history.h             
+				cdo_math.cc               
+				cdo_math.h                
+				cdo_module.cc               
+				cdo_module.h                
+				cdo_options.cc            
+				cdo_options.h             
+				cdo_output.cc             
+				cdo_output.h              
+				cdo_pthread.cc            
+				cdo_pthread.h             
+				cdo_query.cc              
+				cdo_query.h               
+				cdo_read.cc               
+				cdo_rlimit.cc             
+				cdo_rlimit.h              
+				cdo_season.cc             
+				cdo_season.h              
+				cdo_syntax_error.cc       
+				cdo_syntax_error.h        
+				cdo_node_attach_exception.h 
+				cdo_exception.h           
+				cdo_task.cc               
+				cdo_task.h                
+				cdo_timer.h               
+				cdo_varlist.cc            
+				cdo_varlist.h             
+				cdo_vlist.cc              
+				cdo_vlist.h               
+				cdo_zaxis.cc              
+				cdo_zaxis.h               
+				cf_interface.h            
+				cfortran.h                
+				cfortran.h                
+				cimdOmp.cc                
+				cimdOmp.h                 
+				cmortable_parser.cc       
+				color.cc                  
+				color.h                   
+				commandline.cc            
+				commandline.h             
+				compare.h                 
+				const.h                   
+				constants.cc              
+				constants.h               
+				convert_units.cc          
+				convert_units.h           
+				cthread_debug.cc          
+				cthread_debug.h           
+				custom_modules.cc         
+				custom_modules.h          
+				dcw_reader.cc             
+				dcw_reader.h              
+				datarangelist.h           
+				datetime.cc               
+				datetime.h                
+				dmemory.h                 
+				ecacore.cc                
+				ecacore.h                 
+				ecautil.cc                
+				ecautil.h                 
+				eigen_solution.cc         
+				eigen_solution.h          
+				expr.cc                   
+				expr.h                    
+				expr_fun.cc               
+				expr_fun.h                
+				expr_lex.cc               
+				expr_yacc.cc              
+				expr_yacc.hh              
+				factory.h 				  
+				factory.cc				  
+				field.cc                  
+				field.h                   
+				field_functions.h         
+				field2.cc                 
+				field2_complex.cc         
+				fieldc.cc                 
+				fieldc_complex.cc         
+				field_memory.cc           
+				field_meridional.cc       
+				field_zonal.cc            
+				field_vinterp.cc          
+				field_vinterp.h           
+				fileStream.cc             
+				fileStream.h              
+				fill_1d.cc                
+				fill_1d.h                 
+				eof_mode.cc               
+				eof_mode.h	          
+				gaussian_latitudes.c      
+				gaussian_latitudes.h      
+				getMemorySize.c           
+				getRSS.c                  
+				grid_area.cc              
+				grid_define.cc            
+				grid_define.h             
+				grid_from_name.cc         
+				grid_gme.cc               
+				grid_icosphere.cc         
+				grid_point_search.cc      
+				grid_point_search.h       
+				grid_print.cc             
+				grid_read.cc              
+				grid_read_pingo.cc        
+				grid_read_pingo.h         
+				griddes.cc                
+				griddes.h                 
+				griddes_h5.cc             
+				griddes_nc.cc             
+				hetaeta.cc                
+				hetaeta.h                 
+				institution.cc            
+				institution.h             
+				interpol.cc               
+				interpol.h                
+				knn_weights.h             
+				libncl.h                  
+				listbuffer.h              
+				mapping.cc                
+				mapping.h                 
+				matrix_view.h             
+				merge_axis.cc             
+				merge_axis.h              
+				module_info.cc            
+				module_info.h             
+				modules.cc                
+				modules.h                 
+				mpim_grid/grid_convert.h  
+				mpim_grid/grid_proj.cc    
+				mpim_grid/grid_proj.h     
+				mpim_grid/grid_rot.cc     
+				mpim_grid/grid_rot.h      
+				mpim_grid/grid_healpix.cc 
+				mpim_grid/grid_healpix.h  
+				mpim_grid/gridreference.cc
+				mpim_grid/gridreference.h 
+				mpim_grid/mpim_grid.cc    
+				mpim_grid/mpim_grid.h     
+				mpmo.cc                   
+				mpmo.h                    
+				mpmo_color.cc             
+				mpmo_color.h              
+				namelist.cc               
+				namelist.h                
+				nanoflann.hpp             
+				operator_help.cc          
+				operator_help.h           
+				par_io.cc                 
+				par_io.h                  
+				param_conversion.cc       
+				param_conversion.h        
+				parse_literals.cc         
+				parse_literals.h          
+				percentiles.cc            
+				percentiles.h             
+				percentiles_hist.cc       
+				percentiles_hist.h        
+				pipe.cc                   
+				pipe.h                    
+				pipeStream.cc             
+				pipeStream.h              
+				pmlist.cc                 
+				pmlist.h                  
+				printinfo.cc              
+				printinfo.h               
+				process.cc                
+				process.h                 
+				processManager.cc         
+				processManager.h          
+				parser.h                  
+				parser.cc                 
+				node.cc                   
+				node.h                    
+				process_int.cc            
+				process_int.h             
+				progress.cc               
+				progress.h                
+				pthread_debug.cc          
+				pthread_debug.h           
+				region.h                  
+				region.cc                 
+				remap.h                   
+				remap_bicubic.cc          
+				remap_bilinear.cc         
+				remap_cell_search.cc      
+				remap_conserv.cc          
+				remap_conserv_scrip.cc    
+				remap_distwgt.cc          
+				remap_grid_cell_search.cc 
+				remap_grid_cell_search.h  
+				remap_point_search.cc     
+				remap_scrip_io.cc         
+				remap_search_latbins.cc   
+				remap_search_reg2d.cc     
+				remap_store_link.cc       
+				remap_store_link.h        
+				remap_store_link_cnsrv.cc 
+				remap_store_link_cnsrv.h  
+				remap_utils.cc            
+				remap_utils.h             
+				remap_vars.cc             
+				remap_vars.h              
+				remaplib.cc               
+				remapsort.cc              
+				selboxinfo.h              
+				sellist.cc                
+				sellist.h                 
+				specspace.cc              
+				specspace.h               
+				statistic.cc              
+				statistic.h               
+				stdnametable.cc           
+				stdnametable.h            
+				table.cc                  
+				table.h                   
+				timer.cc                  
+				timer.h                   
+				transform.h               
+				util_fileextensions.cc    
+				util_fileextensions.h     
+				util_files.cc             
+				util_files.h              
+				util_string.cc            
+				util_string.h             
+				util_wildcards.cc         
+				util_wildcards.h          
+				util_date.h               
+				varray.cc                 
+				varray.h                  
+				vector3d.h                
+				verifygrid.h              
+				vertical_interp.cc        
+				vertical_interp.h         
+				vertint_util.h		  
+				vertint_util.cc	          
+				zaxis_print.cc
+)
+
+list( APPEND cdolib_src_files
+				json/jsmn.h               
+				json/jsmn.c
+)
+
+list( APPEND cdolib_src_files
+				kdtreelib/kdtree.h                
+				kdtreelib/kdtree_cartesian.cc     
+				kdtreelib/kdtree_common.cc        
+				kdtreelib/kdtree_spherical.cc     
+				kdtreelib/qsort.cc                
+				kdtreelib/pmergesort.cc           
+				kdtreelib/pqueue.cc               
+				kdtreelib/pqueue.h
+)
+
+list( APPEND cdolib_src_files
+		etopo.dat temp.dat mask.dat
+)
+
+add_library(cdolib
+  ${cdolib_src_files}
+)
+
+include_directories("${PROJECT_SOURCE_DIR}/src/mpim_grid"
+                                  "${PROJECT_SOURCE_DIR}/src"
+                                  "${PROJECT_SOURCE_DIR}/libcdi/src"
+)
+
+target_compile_definitions(cdolib PUBLIC HAVE_CONFIG_H restrict= CDI_SIZE_TYPE=size_t YAC_FOR_CDO)
+
+
+list( APPEND cdo_src_files
+				cdo.cc               
+)
+
+list( APPEND cdo_src_files
+                                Adisit.cc            
+				Afterburner.cc       
+				Arith.cc             
+				Arithc.cc            
+				Arithdays.cc         
+				Arithlat.cc          
+				Bitrounding.cc       
+				Cat.cc               
+				CDIread.cc           
+				CDItest.cc           
+				CDIwrite.cc          
+				Change.cc            
+				Change_e5slm.cc      
+				Cloudlayer.cc        
+				CMOR.cc              
+				CMOR_lite.cc         
+				CMOR_table.cc        
+				Collgrid.cc          
+				Command.cc           
+				Comp.cc              
+				Compc.cc             
+				Complextorect.cc     
+				Cond.cc              
+				Cond2.cc             
+				Condc.cc             
+				Consecstat.cc        
+				Copy.cc              
+				DCW_util.cc          
+				Dayarith.cc          
+				Deltat.cc            
+				Deltime.cc           
+				Depth.cc             
+				Derivepar.cc         
+				Detrend.cc           
+				Diff.cc              
+				Distgrid.cc          
+				Duplicate.cc         
+				EOFs.cc              
+				Eof3d.cc             
+				EcaIndices.cc        
+				EcaEtccdi.cc         
+			        Echam5ini.cc         
+				Enlarge.cc           
+				Enlargegrid.cc       
+				Ensstat.cc           
+				Ensstat3.cc          
+				Ensval.cc            
+				Eofcoeff.cc         
+				Eofcoeff3d.cc        
+				EstFreq.cc           
+				Exprf.cc             
+				FC.cc                
+				Filedes.cc           
+				Fillmiss.cc          
+				Filter.cc            
+				Fldrms.cc            
+				Fldstat.cc           
+				Fldstat2.cc          
+				Fourier.cc           
+				Gengrid.cc           
+				Getgridcell.cc       
+				Gradsdes.cc          
+				Gridboxstat.cc       
+				Gridcell.cc          
+				Gridsearch.cc        
+				Harmonic.cc          
+				Healpix.cc           
+				Hi.cc                
+				Histogram.cc         
+				Importamsr.cc        
+				Importbinary.cc      
+				Importcmsaf.cc       
+				Importobs.cc         
+				Importfv3grid.cc     
+				Info.cc              
+				Input.cc             
+				Intgrid.cc           
+				Intgridtraj.cc       
+				Intlevel.cc          
+				Intlevel3d.cc        
+				Intntime.cc          
+				Inttime.cc           
+				Intyear.cc           
+				Invert.cc            
+				Invertlev.cc         
+				Lic.cc               
+				Longinfo.cc          
+				MapReduce.cc         
+				Maskbox.cc           
+				Mastrfu.cc           
+				Math.cc              
+				Merge.cc             
+				Mergegrid.cc         
+				Mergetime.cc         
+				Merstat.cc           
+				Monarith.cc          
+				Mrotuv.cc            
+				Mrotuvb.cc           
+				NCL_wind.cc          
+				Ninfo.cc             
+				Nmldump.cc           
+				Output.cc            
+				Outputgmt.cc         
+				Pack.cc              
+				Pardup.cc            
+				Pinfo.cc             
+				Pressure.cc          
+				Query.cc             
+				Recttocomplex.cc     
+				Regres.cc            
+				Remapeta.cc          
+				Remapgrid.cc         
+				Remapweights.cc      
+				Remapstat.cc         
+				Replace.cc           
+				Replacevalues.cc     
+				Rhopot.cc            
+				Rotuv.cc             
+				Runpctl.cc           
+				Runstat.cc           
+				Samplegrid.cc        
+				Samplegridicon.cc    
+				Seascount.cc         
+				Seaspctl.cc          
+				Seasstat.cc          
+				Seasmonstat.cc       
+				Selbox.cc            
+				Selgridcell.cc       
+				Select.cc            
+				Selmulti.cc         
+				Seloperator.cc       
+				Selrec.cc            
+				Selregion.cc         
+				Selsurface.cc        
+				Seltime.cc           
+				Selvar.cc            
+				Selyearidx.cc        
+				Set.cc               
+				Setattribute.cc      
+				Setbox.cc            
+				Setgrid.cc           
+				Setgridcell.cc       
+				Sethalo.cc           
+				Setmiss.cc          
+				Setpartab.cc         
+				Setrcaname.cc        
+				Settime.cc           
+				Setzaxis.cc          
+				Shiftxy.cc           
+				Showinfo.cc          
+				Showattribute.h      
+				Showattribute.cc     
+				Sinfo.cc             
+				Smooth.cc            
+				Sort.cc              
+				Sorttimestamp.cc     
+				Specinfo.cc          
+				Spectral.cc          
+				Spectrum.cc          
+				Split.cc             
+				Splitdate.cc         
+				Splitrec.cc          
+				Splittime.cc         
+				Splityear.cc         
+				Tee.cc               
+				Test.cc              
+				Tests.cc             
+				Timcount.cc          
+				Timcumsum.cc         
+				Timfillmiss.cc       
+				Timpctl.cc           
+				Timselpctl.cc        
+				Timselstat.cc        
+				Timsort.cc           
+				Timstat.cc           
+				Timstat2.cc          
+				Timstat3.cc          
+				Tinfo.cc             
+				Tocomplex.cc         
+				Transpose.cc         
+				Trend.cc             
+				Trendarith.cc        
+				Tstepcount.cc        
+				Unpack.cc            
+				Vargen.cc            
+				Varrms.cc            
+				Varsstat.cc          
+				Vertfillmiss.cc      
+				Vertintap.cc         
+				Vertintgh.cc         
+				Vertintml.cc         
+				Vertintzs.cc         
+				Vertstat.cc          
+				Vertcum.cc           
+				Vertwind.cc          
+				Verifygrid.cc        
+				Verifyweights.cc     
+				Wct.cc               
+				Wind.cc              
+				WindTrans.cc         
+				Writegrid.cc         
+				Writerandom.cc       
+				XTimstat.cc          
+				Yeararith.cc         
+				Yearmonstat.cc       
+				Ydayarith.cc         
+				Ydaypctl.cc          
+				Ydaystat.cc         
+				Ydrunpctl.cc         
+				Ydrunstat.cc         
+				Yhourarith.cc        
+				Yhourstat.cc         
+				Ymonarith.cc         
+				Ymoncomp.cc          
+				Ymonpctl.cc          
+				Ymonstat.cc          
+				Yseaspctl.cc         
+				Yseasstat.cc         
+				Zonstat.cc
+)
+
+add_executable(cdo
+  ${cdo_src_files}
+)
+
+target_compile_definitions(cdo  PUBLIC HAVE_CONFIG_H  restrict= CDI_SIZE_TYPE=size_t YAC_FOR_CDO)
+
+target_link_libraries(cdo PUBLIC cdilib cdolib yac gradsdes healpix )
diff --git a/src/Cat.cc b/src/Cat.cc
index 6fec6b9fb1c048daaaec604aec6d8cfcfcf9d97c..aff6e36a3831951e4b697b006c4834f8eb522fa6 100644
--- a/src/Cat.cc
+++ b/src/Cat.cc
@@ -21,13 +21,26 @@
 #include "progress.h"
 #include "cdo_options.h"
 
-class ModuleCat
+class Cat : public Process
 {
   enum class StreamMode
   {
     APPEND,
     CREATE
   };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Cat",
+    .operators = { { "cat", CopyHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Cat> registration = RegisterEntry<Cat>(module);
+
   StreamMode streamMode = StreamMode::APPEND;
   bool hasConstVars = true;
   bool dataIsUnchanged = false;
@@ -40,10 +53,8 @@ class ModuleCat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
@@ -53,6 +64,7 @@ public:
 
     progress::init();
   }
+
   void
   run()
   {
@@ -74,8 +86,8 @@ public:
             auto ntsteps = vlistNtsteps(vlistID1);
             if (ntsteps == 1 && varList_numVaryingVars(varList1) == 0) ntsteps = 0;
 
-            bool file_exists = Options::cdoOverwriteMode ? false : FileUtils::file_exists(cdo_get_stream_name(nfiles));
-            if (file_exists)
+            std::string ofilename = cdo_get_stream_name(nfiles);
+            if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename))
               {
                 streamID2 = cdo_open_append(nfiles);
 
@@ -91,7 +103,7 @@ public:
               }
             else
               {
-                if (Options::cdoVerbose) cdo_print("Output file doesn't exist, creating: %s", cdo_get_stream_name(nfiles));
+                if (Options::cdoVerbose) cdo_print("Output file doesn't exist, creating: %s", ofilename);
 
                 streamMode = StreamMode::CREATE;
                 streamID2 = cdo_open_write(nfiles);
@@ -152,6 +164,7 @@ public:
         if (Options::cdoVerbose) cdo_print("Processed file: %s   %.2f seconds", cdo_get_stream_name(indf), timer.elapsed());
       }
   }
+
   void
   close()
   {
@@ -162,17 +175,5 @@ public:
         vlistDestroy(vlistID2);
         taxisDestroy(taxisID2);
       }
-
-    cdo_finish();
   }
 };
-void *
-Cat(void *process)
-{
-  ModuleCat cat;
-  cat.init(process);
-  cat.run();
-  cat.close();
-
-  return nullptr;
-}
diff --git a/src/Change.cc b/src/Change.cc
index eda2638c079d39ed73e8719b3a805ed857e8c718..c505b6f7cafeaf82e3d7d4aa1d1df61ddfe03ea9 100644
--- a/src/Change.cc
+++ b/src/Change.cc
@@ -301,8 +301,29 @@ changeLtype(int vlistID2, int nch, const std::vector<int> &chltypes)
     }
 }
 
-class ModuleChange
+class Change : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Change",
+    .operators = { { "chcode", 0, 0, "pairs of old and new code numbers", ChangeHelp },
+                   { "chtabnum", 0, 0, "pairs of old and new GRIB1 table numbers", ChangeHelp },
+                   { "chparam", 0, 0, "pairs of old and new parameter identifiers", ChangeHelp },
+                   { "chname", 0, 0, "pairs of old and new variable names", ChangeHelp },
+                   { "chunit", 0, 0, "pairs of old and new variable units", ChangeHelp },
+                   { "chlevel", 0, 0, "pairs of old and new levels", ChangeHelp },
+                   { "chlevelc", 0, 0, "code number, old and new level", ChangeHelp },
+                   { "chlevelv", 0, 0, "variable name, old and new level", ChangeHelp },
+                   { "chltype", 0, 0, "pairs of old and new level type", ChangeHelp } },
+    .aliases = { { "chvar", "chname" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Change> registration = RegisterEntry<Change>(module);
+
+  int CHCODE, CHTABNUM, CHPARAM, CHNAME, CHUNIT, CHLEVEL, CHLEVELC, CHLEVELV, CHLTYPE;
   const char *chname = nullptr;
   int chcode = 0;
   std::vector<const char *> chnames;
@@ -320,21 +341,17 @@ class ModuleChange
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    auto CHCODE   = cdo_operator_add("chcode",   0, 0, "pairs of old and new code numbers");
-    auto CHTABNUM = cdo_operator_add("chtabnum", 0, 0, "pairs of old and new GRIB1 table numbers");
-    auto CHPARAM  = cdo_operator_add("chparam",  0, 0, "pairs of old and new parameter identifiers");
-    auto CHNAME   = cdo_operator_add("chname",   0, 0, "pairs of old and new variable names");
-    auto CHUNIT   = cdo_operator_add("chunit",   0, 0, "pairs of old and new variable units");
-    auto CHLEVEL  = cdo_operator_add("chlevel",  0, 0, "pairs of old and new levels");
-    auto CHLEVELC = cdo_operator_add("chlevelc", 0, 0, "code number, old and new level");
-    auto CHLEVELV = cdo_operator_add("chlevelv", 0, 0, "variable name, old and new level");
-    auto CHLTYPE  = cdo_operator_add("chltype",  0, 0, "pairs of old and new type");
-    // clang-format on
+    CHCODE = module.get_id("chcode");
+    CHTABNUM = module.get_id("chtabnum");
+    CHPARAM = module.get_id("chparam");
+    CHNAME = module.get_id("chname");
+    CHUNIT = module.get_id("chunit");
+    CHLEVEL = module.get_id("chlevel");
+    CHLEVELC = module.get_id("chlevelc");
+    CHLEVELV = module.get_id("chlevelv");
+    CHLTYPE = module.get_id("chltype");
 
     auto operatorID = cdo_operator_id();
 
@@ -411,6 +428,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -437,24 +455,11 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Change(void *process)
-{
-  ModuleChange change;
-
-  change.init(process);
-  change.run();
-  change.close();
-
-  return nullptr;
-}
diff --git a/src/Change_e5slm.cc b/src/Change_e5slm.cc
index 0106913387cbb67626b30a6bb23abc50c45eda8a..f0a07d1d2797bea68ce6fecfdfa38497e887636f 100644
--- a/src/Change_e5slm.cc
+++ b/src/Change_e5slm.cc
@@ -16,11 +16,22 @@
 #include "process_int.h"
 #include "cdi_lockedIO.h"
 
-class ModuleChange_e5slm
+class Change_e5slm : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Change_e5slm",
+    .operators = { { "change_e5slm"}, { "change_e5lsm"}, { "change_e5mask"} },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Change_e5slm> registration = RegisterEntry<Change_e5slm>(module);
   int nrecs;
   int varID, levelID;
-  size_t nmiss;
+  size_t numMissVals;
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -38,9 +49,8 @@ class ModuleChange_e5slm
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     streamID1 = cdo_open_read(0);
 
@@ -72,9 +82,9 @@ public:
     streamInqTimestep(streamIDslm, 0);
 
     streamInqRecord(streamIDslm, &varID, &levelID);
-    streamReadRecord(streamIDslm, cland.data(), &nmiss);
+    streamReadRecord(streamIDslm, cland.data(), &numMissVals);
 
-    if (nmiss) cdo_abort("SLM with missing values are unsupported!");
+    if (numMissVals) cdo_abort("SLM with missing values are unsupported!");
 
     const auto mm = varray_min_max(cland);
     if (mm.min < 0 || mm.max > 1) cdo_warning("Values of SLM out of bounds! (minval=%g, maxval=%g)", mm.min, mm.max);
@@ -126,7 +136,7 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             auto code = codes[varID];
             if (code == 172)
@@ -154,7 +164,7 @@ public:
               }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_write_record(streamID2, array.data(), numMissVals);
           }
 
         tsID++;
@@ -165,17 +175,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Change_e5slm(void *process)
-{
-  ModuleChange_e5slm change_e5slm;
-  change_e5slm.init(process);
-  change_e5slm.run();
-  change_e5slm.close();
-  return nullptr;
-}
diff --git a/src/Cloudlayer.cc b/src/Cloudlayer.cc
index 3b23f08064ae76510ab81a46a476ae219d15ea8d..25cf9c84304a83537742cd34d278acc1fbe78b6e 100644
--- a/src/Cloudlayer.cc
+++ b/src/Cloudlayer.cc
@@ -86,8 +86,19 @@ pl_index(long &maxLevIndex, long &minLevIndex, double pmax, double pmin, long nl
       }
 }
 
-class ModuleCloudlayer
+class Cloudlayer : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Cloudlayer",
+    .operators = { { "cloudlayer"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Cloudlayer> registration = RegisterEntry<Cloudlayer>(module);
   static const int NumVars = 3;
   int gridID, zaxisID;
   bool zrev = false;
@@ -114,9 +125,8 @@ class ModuleCloudlayer
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     if (cdo_operator_argc() > 0)
       {
@@ -291,9 +301,9 @@ public:
 
             if (varID == aclcacID)
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, aclcac.data() + offset, &nmiss);
-                if (nmiss != 0) cdo_abort("Missing values unsupported!");
+                size_t numMissVals;
+                cdo_read_record(streamID1, aclcac.data() + offset, &numMissVals);
+                if (numMissVals != 0) cdo_abort("Missing values unsupported!");
               }
           }
 
@@ -309,10 +319,10 @@ public:
 
         for (int varID = 0; varID < nvars2; ++varID)
           {
-            auto nmiss = varray_num_mv(gridsize, cloud[varID], missval);
+            auto numMissVals = varray_num_mv(gridsize, cloud[varID], missval);
 
             cdo_def_record(streamID2, varID, 0);
-            cdo_write_record(streamID2, cloud[varID].data(), nmiss);
+            cdo_write_record(streamID2, cloud[varID].data(), numMissVals);
           }
 
         tsID++;
@@ -325,18 +335,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Cloudlayer(void *process)
-{
-  ModuleCloudlayer cloudlayer;
-
-  cloudlayer.init(process);
-  cloudlayer.run();
-  cloudlayer.close();
-
-  return nullptr;
-}
diff --git a/src/Collgrid.cc b/src/Collgrid.cc
index 2386dba50e02d9a7afc59d8fd6dea8e5b2159764..c8121c5eaf19b9455538d19088e40356333fdb2c 100644
--- a/src/Collgrid.cc
+++ b/src/Collgrid.cc
@@ -5,8 +5,6 @@
 
 */
 
-#include <algorithm>  // sort
-
 #include <cdi.h>
 
 #include "cdo_rlimit.h"
@@ -41,12 +39,6 @@ struct xyinfoType
   int id = -1;
 };
 
-static bool
-cmpx(const xyinfoType &a, const xyinfoType &b)
-{
-  return (a.x < b.x);
-}
-
 static bool
 cmpxy_lt(const xyinfoType &a, const xyinfoType &b)
 {
@@ -138,14 +130,13 @@ gen_coll_grid(int ngrids, int nfiles, std::vector<CollgridInfo> &collgridInfo, i
 
   if (isRegular)
     {
-      std::sort(xyinfo.begin(), xyinfo.end(), cmpx);
+      ranges::sort(xyinfo, {}, &xyinfoType::x);
 
       if (Options::cdoVerbose)
         for (int fileID = 0; fileID < nfiles; ++fileID)
           printf("2 %d %g %g \n", xyinfo[fileID].id, xyinfo[fileID].x, xyinfo[fileID].y);
 
-      auto cmpxy = isSouthNorth ? cmpxy_lt : cmpxy_gt;
-      std::sort(xyinfo.begin(), xyinfo.end(), cmpxy);
+      ranges::sort(xyinfo, isSouthNorth ? cmpxy_lt : cmpxy_gt);
 
       if (Options::cdoVerbose)
         for (int fileID = 0; fileID < nfiles; ++fileID)
@@ -430,8 +421,20 @@ select_vars(const std::vector<bool> &selectedVars, int vlistID1, const VarList &
   if (numVars == 0) cdo_abort("No variables selected!");
 }
 
-class ModuleCollgrid
+class Collgrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Collgrid",
+    .operators = { { "collgrid", CollgridHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Collgrid> registration = RegisterEntry<Collgrid>(module);
+
   int nxblocks = -1;
 
   CdoStreamID streamID1;
@@ -454,10 +457,8 @@ class ModuleCollgrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     nfiles = cdo_stream_cnt() - 1;
     std::string ofilename = cdo_get_stream_name(nfiles);
 
@@ -482,7 +483,8 @@ public:
     vlistClearFlag(vlistID1);
 
     // check that the contents is always the same
-    for (int fileID = 1; fileID < nfiles; ++fileID) vlist_compare(vlistID1, collgridInfo[fileID].vlistID, CmpVlist::Name | CmpVlist::NumLevels);
+    for (int fileID = 1; fileID < nfiles; ++fileID)
+      vlist_compare(vlistID1, collgridInfo[fileID].vlistID, CmpVlist::Name | CmpVlist::NumLevels);
 
     auto nsel = cdo_operator_argc();
     int noff = 0;
@@ -615,8 +617,8 @@ public:
                   {
                     auto patchSize = collgridInfo[fileID].varList[varID].gridsize;
                     if (cellIndex.size() < patchSize) cellIndex.resize(patchSize);
-                    size_t nmiss;
-                    cdo_read_record(collgridInfo[fileID].streamID, cellIndex.data(), &nmiss);
+                    size_t numMissVals;
+                    cdo_read_record(collgridInfo[fileID].streamID, cellIndex.data(), &numMissVals);
                     for (size_t i = 0; i < patchSize; ++i)
                       collgridInfo[fileID].cellIndex[gindex][i] = std::lround(cellIndex[i]) - 1;
                   }
@@ -665,18 +667,5 @@ public:
     for (int fileID = 0; fileID < nfiles; ++fileID) cdo_stream_close(collgridInfo[fileID].streamID);
 
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Collgrid(void *process)
-{
-  ModuleCollgrid collgrid;
-  collgrid.init(process);
-  collgrid.run();
-  collgrid.close();
-
-  return nullptr;
-}
diff --git a/src/Command.cc b/src/Command.cc
index c6ef001a2885e968d84b9507e4addc3c789537e2..508744c9654aa9012ef850c3a40f17cd8d5789ca 100644
--- a/src/Command.cc
+++ b/src/Command.cc
@@ -160,13 +160,13 @@ com_stat(const std::string &arg)
 
           auto levelID = (nlevels > 1) ? gl_levelID : 0;
 
-          size_t nmiss;
-          streamReadVarSlice(gl_streamID, gl_varID, levelID, gl_data.data(), &nmiss);
+          size_t numMissVals;
+          streamReadVarSlice(gl_streamID, gl_varID, levelID, gl_data.data(), &numMissVals);
 
           auto mmm = varray_min_max_mean_mv(gridsize, gl_data, missval);
 
-          fprintf(stdout, "%s:  z=%d  t=%d  size=%zu nmiss=%zu  min=%.5g mean=%.5g max=%.5g [%.2fs]\n", name, levelID + 1, tsID + 1,
-                  gridsize, nmiss, mmm.min, mmm.mean, mmm.max, stepTimer.elapsed());
+          fprintf(stdout, "%s:  z=%d  t=%d  size=%zu numMissVals=%zu  min=%.5g mean=%.5g max=%.5g [%.2fs]\n", name, levelID + 1, tsID + 1,
+                  gridsize, numMissVals, mmm.min, mmm.mean, mmm.max, stepTimer.elapsed());
         }
     }
 
@@ -389,9 +389,9 @@ execute_line(const std::string &line)
 
   // Isolate the command word.
   int i = 0;
-  while (line[i] && isspace(line[i])) i++;
+  while (line[i] && std::isspace(line[i])) i++;
   int pos = i;
-  while (line[i] && !isspace(line[i])) i++;
+  while (line[i] && !std::isspace(line[i])) i++;
   int count = i;
 
   auto word = line.substr(pos, count);
@@ -403,7 +403,7 @@ execute_line(const std::string &line)
       return -1;
     }
   // Get argument to command, if any.
-  while (isspace(line[i])) i++;
+  while (std::isspace(line[i])) i++;
 
   pos = i;
   auto args = line.substr(pos);
@@ -446,9 +446,9 @@ peakRSS_string()
 }
 
 static void
-read_line(const std::string &prompt, std::string &line)
+read_line(const std::string &p_prompt, std::string &line)
 {
-  fputs(prompt.c_str(), stdout);
+  fputs(p_prompt.c_str(), stdout);
   if (Options::cdoVerbose)
     {
       fputs(" [", stdout);
@@ -499,13 +499,24 @@ run_demo()
     }
 }
 
-class ModuleCommand
+class Command : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Command",
+    .operators = { { "command"}, { "com"}, { "cmd"} },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Command> registration = RegisterEntry<Command>(module);
+
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     if (cdo_operator_argc() == 1)
       {
@@ -528,11 +539,11 @@ public:
     else
       {
         // Loop reading and executing lines until the user quits.
-        const std::string prompt = "cdo cmd";
+        const std::string custom_prompt = "cdo cmd";
         while (!Done)
           {
             std::string line;
-            read_line(prompt, line);
+            read_line(custom_prompt, line);
             execute_line(trim(line));
           }
       }
@@ -541,17 +552,5 @@ public:
   close()
   {
     streamClose(gl_streamID);
-    cdo_finish();
   }
 };
-
-void *
-Command(void *process)
-{
-  ModuleCommand command;
-  command.init(process);
-  command.run();
-  command.close();
-
-  return nullptr;
-}
diff --git a/src/Comp.cc b/src/Comp.cc
index 44b5ab021cf994a8cbbee738677f88c56d7241f4..47069a33e792293239f5cbd88d16d8664505c7a2 100644
--- a/src/Comp.cc
+++ b/src/Comp.cc
@@ -43,7 +43,7 @@ static auto func_comp
           }
       };
 
-class ModuleComp
+class Comp : public Process
 {
   enum
   {
@@ -51,6 +51,23 @@ class ModuleComp
     FILL_TS,
     FILL_REC
   };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Comp",
+    .operators = { { "eq", FieldFunc_EQ, 0, CompHelp },
+                   { "ne", FieldFunc_NE, 0, CompHelp },
+                   { "le", FieldFunc_LE, 0, CompHelp },
+                   { "lt", FieldFunc_LT, 0, CompHelp },
+                   { "ge", FieldFunc_GE, 0, CompHelp },
+                   { "gt", FieldFunc_GT, 0, CompHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Comp> registration = RegisterEntry<Comp>(module);
   int filltype = FILL_NONE;
   Varray2D<double> vardata;
 
@@ -71,16 +88,8 @@ class ModuleComp
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("eq", FieldFunc_EQ, 0, nullptr);
-    cdo_operator_add("ne", FieldFunc_NE, 0, nullptr);
-    cdo_operator_add("le", FieldFunc_LE, 0, nullptr);
-    cdo_operator_add("lt", FieldFunc_LT, 0, nullptr);
-    cdo_operator_add("ge", FieldFunc_GE, 0, nullptr);
-    cdo_operator_add("gt", FieldFunc_GT, 0, nullptr);
 
     auto operatorID = cdo_operator_id();
     operFunc = cdo_operator_f1(operatorID);
@@ -192,17 +201,17 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            size_t nmiss1 = 0, nmiss2 = 0;
+            size_t numMissVals1 = 0, numMissVals2 = 0;
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, arrayx1, &nmiss1);
+            cdo_read_record(streamID1, arrayx1, &numMissVals1);
 
             if (tsID == 0 || filltype == FILL_NONE)
               {
                 if (recID == 0 || filltype != FILL_REC)
                   {
                     cdo_inq_record(streamID2, &varID, &levelID);
-                    cdo_read_record(streamID2, arrayx2, &nmiss2);
+                    cdo_read_record(streamID2, arrayx2, &numMissVals2);
                   }
 
                 if (filltype == FILL_TS)
@@ -245,10 +254,10 @@ public:
                   }
               }
 
-            if (nmiss1 > 0) cdo_check_missval(missval1, varList1[varID].name);
-            // if (nmiss2 > 0) cdo_check_missval(missval2, varList2[varID].name);
+            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1[varID].name);
+            // if (numMissVals2 > 0) cdo_check_missval(missval2, varList2[varID].name);
 
-            auto hasMissvals = (nmiss1 > 0 || nmiss2 > 0);
+            auto hasMissvals = (numMissVals1 > 0 || numMissVals2 > 0);
             // clang-format off
             if      (operFunc == FieldFunc_EQ) func_comp(hasMissvals, ngp, missval1, missval2, vaIn1, vaIn2, vaOut, binary_op_EQ);
             else if (operFunc == FieldFunc_NE) func_comp(hasMissvals, ngp, missval1, missval2, vaIn1, vaIn2, vaOut, binary_op_NE);
@@ -259,9 +268,9 @@ public:
             else cdo_abort("Operator not implemented!");
             // clang-format on
 
-            auto nmissOut = varray_num_mv(ngp, vaOut, missval1);
+            auto numMissValsOut = varray_num_mv(ngp, vaOut, missval1);
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, vaOut.data(), nmissOut);
+            cdo_write_record(streamID3, vaOut.data(), numMissValsOut);
           }
 
         tsID++;
@@ -274,18 +283,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Comp(void *process)
-{
-  ModuleComp comp;
-  comp.init(process);
-  comp.run();
-  comp.close();
-
-  return nullptr;
-}
diff --git a/src/Compc.cc b/src/Compc.cc
index 55d5f65fb55383f62929eba5319eab702b94202a..a6748e42a3bb00461976f5fa41147f73b5f9e5c3 100644
--- a/src/Compc.cc
+++ b/src/Compc.cc
@@ -28,8 +28,7 @@ static auto func_compc = [](auto hasMissvals, auto n, auto mv, auto &v, const au
     {
       auto constantIsMissval = dbl_is_equal(cVal, mv);
       if (std::isnan(mv))
-        for (size_t i = 0; i < n; ++i)
-          v[i] = (dbl_is_equal(v[i], mv) || constantIsMissval) ? mv : binary_operator(v[i], cVal);
+        for (size_t i = 0; i < n; ++i) v[i] = (dbl_is_equal(v[i], mv) || constantIsMissval) ? mv : binary_operator(v[i], cVal);
       else
         for (size_t i = 0; i < n; ++i) v[i] = (is_equal(v[i], mv) || constantIsMissval) ? mv : binary_operator(v[i], cVal);
     }
@@ -63,19 +62,24 @@ comp_function(Field &field, int operFunc, bool hasMissvals, double rconst)
     comp_function(operFunc, hasMissvals, field.size, field.missval, field.vec_d, rconst);
 }
 
-static void
-add_operators(void)
-{
-  cdo_operator_add("eqc", FieldFunc_EQ, 0, nullptr);
-  cdo_operator_add("nec", FieldFunc_NE, 0, nullptr);
-  cdo_operator_add("lec", FieldFunc_LE, 0, nullptr);
-  cdo_operator_add("ltc", FieldFunc_LT, 0, nullptr);
-  cdo_operator_add("gec", FieldFunc_GE, 0, nullptr);
-  cdo_operator_add("gtc", FieldFunc_GT, 0, nullptr);
-}
-
-class ModuleCompc
+class Compc : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Compc",
+    .operators = { { "eqc", FieldFunc_EQ, 0, CompcHelp },
+                   { "nec", FieldFunc_NE, 0, CompcHelp },
+                   { "lec", FieldFunc_LE, 0, CompcHelp },
+                   { "ltc", FieldFunc_LT, 0, CompcHelp },
+                   { "gec", FieldFunc_GE, 0, CompcHelp },
+                   { "gtc", FieldFunc_GT, 0, CompcHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Compc> registration = RegisterEntry<Compc>(module);
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -89,11 +93,8 @@ class ModuleCompc
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operFunc = cdo_operator_f1(operatorID);
@@ -142,10 +143,10 @@ public:
             cdo_read_record(streamID1, field);
 
             auto missval = field.missval;
-            auto nmiss = field.nmiss;
-            auto hasMissvals = (nmiss > 0 || dbl_is_equal(rconst, missval));
+            auto numMissVals = field.numMissVals;
+            auto hasMissvals = (numMissVals > 0 || dbl_is_equal(rconst, missval));
 
-            if (nmiss > 0) cdo_check_missval(missval, varList[varID].name);
+            if (numMissVals > 0) cdo_check_missval(missval, varList[varID].name);
 
             comp_function(field, operFunc, hasMissvals, rconst);
 
@@ -164,18 +165,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Compc(void *process)
-{
-  ModuleCompc compc;
-  compc.init(process);
-  compc.run();
-  compc.close();
-
-  return nullptr;
-}
diff --git a/src/Complextorect.cc b/src/Complextorect.cc
index 413b1c2a016a4609e20880456e8e4ff922eeb2cd..ffd26b59c0015974b80cad22e58598c64b44b34f 100644
--- a/src/Complextorect.cc
+++ b/src/Complextorect.cc
@@ -10,8 +10,20 @@
 #include "arithmetic.h"
 #include "process_int.h"
 
-class ModuleComplextorect
+class Complextorect : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Complextorect",
+    .operators = { { "complextorect"}, { "complextopol"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_COMP,  // Allowed number type
+    .constraints = { 1, 2, OnlyFirst },
+  };
+  inline static RegisterEntry<Complextorect> registration = RegisterEntry<Complextorect>(module);
+  int COMPLEXTORECT, COMPLEXTOPOL;
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -31,16 +43,13 @@ class ModuleComplextorect
   Varray<double> array3;
   VarList varList1;
 
-  int COMPLEXTORECT, COMPLEXTOPOL;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    COMPLEXTORECT = cdo_operator_add("complextorect", 0, 0, nullptr);
-    COMPLEXTOPOL = cdo_operator_add("complextopol", 0, 0, nullptr);
+    COMPLEXTORECT = module.get_id("complextorect");
+    COMPLEXTOPOL = module.get_id("complextopol");
 
     operatorID = cdo_operator_id();
 
@@ -104,8 +113,8 @@ public:
 
             auto gridsize = varList1[varID].gridsize;
 
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto is_EQ = dbl_is_equal;
             auto missval1 = varList1[varID].missval;
@@ -130,8 +139,8 @@ public:
                   }
               }
 
-            cdo_write_record(streamID2, array2.data(), nmiss);
-            cdo_write_record(streamID3, array3.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
+            cdo_write_record(streamID3, array3.data(), numMissVals);
           }
 
         tsID++;
@@ -147,17 +156,5 @@ public:
 
     vlistDestroy(vlistID2);
     vlistDestroy(vlistID3);
-
-    cdo_finish();
   }
 };
-void *
-Complextorect(void *process)
-{
-  ModuleComplextorect complextorect;
-  complextorect.init(process);
-  complextorect.run();
-  complextorect.close();
-
-  return nullptr;
-}
diff --git a/src/Cond.cc b/src/Cond.cc
index 1ef590626f50c132b42c57e3166763c8243cec93..cffc7beb029d2e2b7f747fcc37623752715f5d19 100644
--- a/src/Cond.cc
+++ b/src/Cond.cc
@@ -37,7 +37,7 @@ operator_IFNOTTHEN(size_t n, double mv1, double mv2, const Varray<double> &vIn1,
     for (size_t i = 0; i < n; ++i) vOut[i] = (!is_equal(vIn1[i], mv1) && is_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
 }
 
-class ModuleCond
+class Cond : public Process
 {
   enum
   {
@@ -45,9 +45,22 @@ class ModuleCond
     FILL_TS,
     FILL_REC
   };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Cond",
+    .operators = { { "ifthen", CondHelp }, { "ifnotthen", CondHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Cond> registration = RegisterEntry<Cond>(module);
+  int IFTHEN, IFNOTTHEN;
   int filltype = FILL_NONE;
   double missval1 = -9.E33;
-  Varray2D<size_t> varnmiss1;
+  Varray2D<size_t> varnumMissVals1;
   Varray2D<double> vardata1;
 
   CdoStreamID streamID1;
@@ -62,17 +75,15 @@ class ModuleCond
 
   VarList varList1, varList2;
   Varray<double> vaIn1, vaIn2, vaOut;
-  int IFTHEN, IFNOTTHEN;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    IFTHEN    = cdo_operator_add("ifthen",    0, 0, nullptr);
-    IFNOTTHEN = cdo_operator_add("ifnotthen", 0, 0, nullptr);
+IFTHEN = module.get_id("ifthen");
+IFNOTTHEN = module.get_id("ifnotthen");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -127,7 +138,7 @@ public:
             filltype = FILL_TS;
             cdo_print("Filling up stream1 >%s< by copying the first timestep.", cdo_get_stream_name(0));
 
-            cdo_fill_ts(vlistID1, vardata1, varnmiss1);
+            cdo_fill_ts(vlistID1, vardata1, varnumMissVals1);
           }
       }
   }
@@ -154,36 +165,36 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID2, &varID, &levelID);
-            size_t nmiss1 = 0, nmiss2;
-            cdo_read_record(streamID2, &vaIn2[0], &nmiss2);
+            size_t numMissVals1 = 0, numMissVals2;
+            cdo_read_record(streamID2, &vaIn2[0], &numMissVals2);
 
             if (tsID == 0 || filltype == FILL_NONE)
               {
                 if (recID == 0 || filltype != FILL_REC)
                   {
                     cdo_inq_record(streamID1, &varID, &levelID);
-                    cdo_read_record(streamID1, &vaIn1[0], &nmiss1);
+                    cdo_read_record(streamID1, &vaIn1[0], &numMissVals1);
                   }
 
                 if (filltype == FILL_TS)
                   {
                     auto offset = varList1[varID].gridsize * levelID;
                     array_copy(varList1[varID].gridsize, &vaIn1[0], &vardata1[varID][offset]);
-                    varnmiss1[varID][levelID] = nmiss1;
+                    varnumMissVals1[varID][levelID] = numMissVals1;
                   }
               }
             else if (filltype == FILL_TS)
               {
                 auto offset = varList1[varID].gridsize * levelID;
                 array_copy(varList1[varID].gridsize, &vardata1[varID][offset], &vaIn1[0]);
-                nmiss1 = varnmiss1[varID][levelID];
+                numMissVals1 = varnumMissVals1[varID][levelID];
               }
 
             auto ngp = varList2[varID].gridsize;
             auto missval2 = varList2[varID].missval;
             if (recID == 0 || filltype != FILL_REC) missval1 = varList1[varID].missval;
 
-            if (nmiss1 > 0) cdo_check_missval(missval1, varList1[varID].name);
+            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1[varID].name);
 
             // clang-format off
             if      (operatorID == IFTHEN)    operator_IFTHEN(ngp, missval1, missval2, vaIn1, vaIn2, vaOut);
@@ -191,9 +202,9 @@ public:
             else cdo_abort("Operator not implemented!");
             // clang-format on
 
-            auto nmiss3 = varray_num_mv(ngp, vaOut, missval2);
+            auto numMissVals3 = varray_num_mv(ngp, vaOut, missval2);
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, vaOut.data(), nmiss3);
+            cdo_write_record(streamID3, vaOut.data(), numMissVals3);
           }
 
         tsID++;
@@ -206,18 +217,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Cond(void *process)
-{
-  ModuleCond cond;
-  cond.init(process);
-  cond.run();
-  cond.close();
-
-  return nullptr;
-}
diff --git a/src/Cond2.cc b/src/Cond2.cc
index c823d91a8f1adcd00d4d3f03a0d8454f15b120dd..ca1ffa20ef45d3fdbf43e0450204c2995fe62de1 100644
--- a/src/Cond2.cc
+++ b/src/Cond2.cc
@@ -18,7 +18,7 @@
 #include "cdo_vlist.h"
 #include "cdo_fill.h"
 
-class ModuleCond2
+class Cond2 : public Process
 {
   enum
   {
@@ -27,10 +27,22 @@ class ModuleCond2
     FILL_REC
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Cond2",
+    .operators = { { "ifthenelse", Cond2Help } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Cond2> registration = RegisterEntry<Cond2>(module);
+
   int filltype = FILL_NONE;
   double missval1 = -9.E33;
-  size_t nmiss1 = 0;
-  Varray2D<size_t> varnmiss1;
+  size_t numMissVals1 = 0;
+  Varray2D<size_t> varnumMissVals1;
   Varray2D<double> vardata1;
 
   CdoStreamID streamID1;
@@ -45,11 +57,8 @@ class ModuleCond2
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("ifthenelse", 0, 0, nullptr);
 
     operator_check_argc(0);
 
@@ -107,7 +116,7 @@ public:
             filltype = FILL_TS;
             cdo_print("Filling up stream1 >%s< by copying the first timestep.", cdo_get_stream_name(0));
 
-            cdo_fill_ts(vlistID1, vardata1, varnmiss1);
+            cdo_fill_ts(vlistID1, vardata1, varnumMissVals1);
           }
       }
   }
@@ -137,18 +146,18 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID2, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID2, &array2[0], &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID2, &array2[0], &numMissVals);
 
             cdo_inq_record(streamID3, &varID, &levelID);
-            cdo_read_record(streamID3, &array3[0], &nmiss);
+            cdo_read_record(streamID3, &array3[0], &numMissVals);
 
             if (tsID == 0 || filltype == FILL_NONE)
               {
                 if (recID == 0 || filltype != FILL_REC)
                   {
                     cdo_inq_record(streamID1, &varID, &levelID);
-                    cdo_read_record(streamID1, &array1[0], &nmiss1);
+                    cdo_read_record(streamID1, &array1[0], &numMissVals1);
                   }
 
                 if (filltype == FILL_TS)
@@ -156,7 +165,7 @@ public:
                     auto gridsize = varList1[varID].gridsize;
                     auto offset = gridsize * levelID;
                     array_copy(gridsize, &array1[0], &vardata1[varID][offset]);
-                    varnmiss1[varID][levelID] = nmiss1;
+                    varnumMissVals1[varID][levelID] = numMissVals1;
                   }
               }
             else if (filltype == FILL_TS)
@@ -164,21 +173,21 @@ public:
                 auto gridsize = varList1[varID].gridsize;
                 auto offset = gridsize * levelID;
                 array_copy(gridsize, &vardata1[varID][offset], &array1[0]);
-                nmiss1 = varnmiss1[varID][levelID];
+                numMissVals1 = varnumMissVals1[varID][levelID];
               }
 
             auto gridsize = varList2[varID].gridsize;
             auto missval2 = varList2[varID].missval;
             if (recID == 0 || filltype != FILL_REC) missval1 = varList1[varID].missval;
 
-            if (nmiss1 > 0) cdo_check_missval(missval1, varList1[varID].name);
+            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1[varID].name);
 
             for (size_t i = 0; i < gridsize; ++i)
               array4[i] = DBL_IS_EQUAL(array1[i], missval1) ? missval2 : !DBL_IS_EQUAL(array1[i], 0.) ? array2[i] : array3[i];
 
-            nmiss = varray_num_mv(gridsize, array4, missval2);
+            numMissVals = varray_num_mv(gridsize, array4, missval2);
             cdo_def_record(streamID4, varID, levelID);
-            cdo_write_record(streamID4, array4.data(), nmiss);
+            cdo_write_record(streamID4, array4.data(), numMissVals);
           }
 
         tsID++;
@@ -192,18 +201,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Cond2(void *process)
-{
-  ModuleCond2 cond2;
-  cond2.init(process);
-  cond2.run();
-  cond2.close();
-
-  return nullptr;
-}
diff --git a/src/Condc.cc b/src/Condc.cc
index 009c168aaf17b5c5ee64d4c82a3a3e74dddc4c42..3acc2e29d9a5baeb3b50c09155d52a141ae7f23a 100644
--- a/src/Condc.cc
+++ b/src/Condc.cc
@@ -40,8 +40,20 @@ operator_IFNOTTHENC(size_t n, T mv, Varray<T> &v, T cVal)
     for (size_t i = 0; i < n; ++i) v[i] = (!is_equal(v[i], mv) && is_equal(v[i], nullVal)) ? cVal : mv;
 }
 
-class ModuleCondc
+class Condc : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Condc",
+    .operators = { { "ifthenc", CondcHelp }, { "ifnotthenc", CondcHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Condc> registration = RegisterEntry<Condc>(module);
+  int IFTHENC, IFNOTTHENC;
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -53,17 +65,14 @@ class ModuleCondc
 
   VarList varList;
 
-  int IFTHENC, IFNOTTHENC;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    IFTHENC    = cdo_operator_add("ifthenc",    0, 0, nullptr);
-    IFNOTTHENC = cdo_operator_add("ifnotthenc", 0, 0, nullptr);
+IFTHENC = module.get_id("ifthenc");
+IFNOTTHENC = module.get_id("ifnotthenc");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -112,8 +121,8 @@ public:
             auto missval = field.missval;
             auto ngp = field.size;
 
-            auto nmiss = field.nmiss;
-            if (nmiss > 0) cdo_check_missval(missval, varList[varID].name);
+            auto numMissVals = field.numMissVals;
+            if (numMissVals > 0) cdo_check_missval(missval, varList[varID].name);
 
             // clang-format off
             if (field.memType == MemType::Float)
@@ -130,7 +139,7 @@ public:
               }
             // clang-format on
 
-            if (nmiss > 0) field_num_mv(field);
+            if (numMissVals > 0) field_num_mv(field);
 
             cdo_def_record(streamID2, varID, levelID);
             cdo_write_record(streamID2, field);
@@ -145,18 +154,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Condc(void *process)
-{
-  ModuleCondc condc;
-  condc.init(process);
-  condc.run();
-  condc.close();
-
-  return nullptr;
-}
diff --git a/src/Consecstat.cc b/src/Consecstat.cc
index ac12479b9f7654121ef29290bc3e3f1fae5023c9..0739b689ed42b61cb9b114a83dc04f862aee07f6 100644
--- a/src/Consecstat.cc
+++ b/src/Consecstat.cc
@@ -51,7 +51,7 @@ selEndOfPeriod(Field &periods, const Field &history, const Field &current, int i
 
   if (!isLastTimestep)
     {
-      if (current.nmiss || history.nmiss)
+      if (current.numMissVals || history.numMissVals)
         {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
@@ -79,7 +79,7 @@ selEndOfPeriod(Field &periods, const Field &history, const Field &current, int i
     }
   else
     {
-      if (current.nmiss)
+      if (current.numMissVals)
         {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
@@ -99,11 +99,22 @@ selEndOfPeriod(Field &periods, const Field &history, const Field &current, int i
         }
     }
 
-  periods.nmiss = varray_num_mv(len, parray, pmissval);
+  periods.numMissVals = varray_num_mv(len, parray, pmissval);
 }
 
-class ModuleConsecstat
+class Consecstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Consecstat",
+    .operators = { { "consects", CONSECTS, 0, ConsecstatHelp }, { "consecsum", CONSECSUM, 0, "refval", ConsecstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Consecstat> registration = RegisterEntry<Consecstat>(module);
   CdiDateTime vDateTime{};
   CdiDateTime histDateTime{};
   double refval = 0.0;
@@ -126,11 +137,8 @@ class ModuleConsecstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("consecsum", CONSECSUM, 0, "refval");
-    cdo_operator_add("consects", CONSECTS, 0, nullptr);
     operatorID = cdo_operator_id();
 
     if (operatorID == CONSECSUM)
@@ -193,7 +201,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(istreamID, &varID, &levelID);
-            cdo_read_record(istreamID, field.vec_d.data(), &field.nmiss);
+            cdo_read_record(istreamID, field.vec_d.data(), &field.numMissVals);
             field.grid = varList[varID].gridID;
             field.size = varList[varID].gridsize;
             field.missval = varList[varID].missval;
@@ -204,14 +212,14 @@ public:
               {
               case CONSECSUM:
                 cdo_def_record(ostreamID, varID, levelID);
-                cdo_write_record(ostreamID, vars[varID][levelID].vec_d.data(), vars[varID][levelID].nmiss);
+                cdo_write_record(ostreamID, vars[varID][levelID].vec_d.data(), vars[varID][levelID].numMissVals);
                 break;
               case CONSECTS:
                 if (itsID != 0)
                   {
                     selEndOfPeriod(periods[varID][levelID], hist[varID][levelID], vars[varID][levelID], false);
                     cdo_def_record(ostreamID, varID, levelID);
-                    cdo_write_record(ostreamID, periods[varID][levelID].vec_d.data(), periods[varID][levelID].nmiss);
+                    cdo_write_record(ostreamID, periods[varID][levelID].vec_d.data(), periods[varID][levelID].numMissVals);
                   }
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -242,7 +250,7 @@ public:
               {
                 selEndOfPeriod(periods[varID][levelID], hist[varID][levelID], vars[varID][levelID], true);
                 cdo_def_record(ostreamID, varID, levelID);
-                cdo_write_record(ostreamID, periods[varID][levelID].vec_d.data(), periods[varID][levelID].nmiss);
+                cdo_write_record(ostreamID, periods[varID][levelID].vec_d.data(), periods[varID][levelID].numMissVals);
               }
           }
       }
@@ -252,18 +260,5 @@ public:
   {
     cdo_stream_close(istreamID);
     cdo_stream_close(ostreamID);
-
-    cdo_finish();
   }
 };
-void *
-Consecstat(void *process)
-{
-  ModuleConsecstat consecstat;
-
-  consecstat.init(process);
-  consecstat.run();
-  consecstat.close();
-
-  return nullptr;
-}
diff --git a/src/Copy.cc b/src/Copy.cc
index 658162b59372df3ee14bb5de8d708aba8702a6f5..acfe478e5346da4a9efe681a47bc432dd7b04593 100644
--- a/src/Copy.cc
+++ b/src/Copy.cc
@@ -27,7 +27,7 @@ is_fdb_stream(const std::string &filename)
 bool
 is_fdb_copy(bool dataIsUnchanged, int nfiles)
 {
-  bool isFdbCopy = false;
+  auto isFdbCopy = false;
 
   if (dataIsUnchanged)
     {
@@ -38,8 +38,25 @@ is_fdb_copy(bool dataIsUnchanged, int nfiles)
   return isFdbCopy;
 }
 
-class ModuleCopy
+class Copy : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Copy",
+    // clang-format off
+    .operators = { { "copy", CopyHelp },
+                   { "clone", CopyHelp },
+                   { "szip", CopyHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Copy>(module);
+
+  int CLONE, SZIP;
 
   bool hasConstantFields = true;
   CdoStreamID streamID2 = CDO_STREAM_UNDEF;
@@ -54,21 +71,14 @@ class ModuleCopy
 
   int operatorID;
 
-  int CLONE, SZIP;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-                 cdo_operator_add("copy",  0, 0, nullptr);
-   CLONE   = cdo_operator_add("clone", 0, 0, nullptr);
-   SZIP    = cdo_operator_add("szip",  0, 0, nullptr);
-    // clang-format on
+    CLONE = module.get_id("clone");
+    SZIP = module.get_id("szip");
 
     operatorID = cdo_operator_id();
     if (operatorID == SZIP)
@@ -86,10 +96,10 @@ public:
 
     progress::init();
   }
+
   void
   run()
   {
-
     int tsID2 = 0;
     for (int indf = 0; indf < nfiles; ++indf)
       {
@@ -140,7 +150,7 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                double fstatus = indf + ((ntsteps > 1) ? (tsID1 + (recID + 1.0) / nrecs) / ntsteps : 1.0);
+                double fstatus = indf + ((ntsteps >= 0) ? (tsID1 + (recID + 1.0) / nrecs) / ntsteps : 1.0);
                 if (!Options::cdoVerbose) progress::update(0, 1, fstatus / nfiles);
 
                 int varID, levelID;
@@ -174,20 +184,8 @@ public:
   void
   close()
   {
-
     cdo_stream_close(streamID2);
 
     if (vlistID2 != CDI_UNDEFID) vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Copy(void *process)
-{
-  ModuleCopy copy;
-  copy.init(process);
-  copy.run();
-  copy.close();
-  return nullptr;
-}
diff --git a/src/DCW_util.cc b/src/DCW_util.cc
index 04f3600668af41616b93b60f41ea79cc8c98992f..55b9101b974440c46c18dbfc59676b6d71b1dca1 100644
--- a/src/DCW_util.cc
+++ b/src/DCW_util.cc
@@ -32,16 +32,26 @@ print_polygons(DCW_Lists &dcw_lists, const std::string &codeNames)
   dcw_print_polygons(dcw_lists, codeList);
 }
 
-class ModuleDCW_util
+class DCW_util : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "DCW_util",
+    .operators = { { "dcw"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 0, NoRestriction },
+  };
+  inline static RegisterEntry<DCW_util> registration = RegisterEntry<DCW_util>(module);
   int argc = 0;
   DCW_Lists dcw_lists;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     if (dcw_load_lists(dcw_lists)) cdo_abort("dcw_load_lists() failed!");
 
@@ -74,17 +84,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-DCW_util(void *process)
-{
-  ModuleDCW_util dcw_util;
-  dcw_util.init(process);
-  dcw_util.run();
-  dcw_util.close();
-
-  return nullptr;
-}
diff --git a/src/Dayarith.cc b/src/Dayarith.cc
index dc15696607ecd96a4c3b16505087ec4961f0a7de..2466d0c73f9623019553b111fbf87ce68095d2a1 100644
--- a/src/Dayarith.cc
+++ b/src/Dayarith.cc
@@ -20,17 +20,22 @@
 #include "cdo_vlist.h"
 #include "field_functions.h"
 
-static void
-add_operators(void)
-{
-  cdo_operator_add("dayadd", FieldFunc_Add, 0, nullptr);
-  cdo_operator_add("daysub", FieldFunc_Sub, 0, nullptr);
-  cdo_operator_add("daymul", FieldFunc_Mul, 0, nullptr);
-  cdo_operator_add("daydiv", FieldFunc_Div, 0, nullptr);
-}
-
-class ModuleDayarith
+class Dayarith : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Dayarith",
+    .operators = { { "dayadd", FieldFunc_Add, 0, DayarithHelp },
+                   { "daysub", FieldFunc_Sub, 0, DayarithHelp },
+                   { "daymul", FieldFunc_Mul, 0, DayarithHelp },
+                   { "daydiv", FieldFunc_Div, 0, DayarithHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Dayarith> registration = RegisterEntry<Dayarith>(module);
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -48,11 +53,8 @@ class ModuleDayarith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -150,18 +152,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Dayarith(void *process)
-{
-  ModuleDayarith dayarith;
-  dayarith.init(process);
-  dayarith.run();
-  dayarith.close();
-
-  return nullptr;
-}
diff --git a/src/Deltat.cc b/src/Deltat.cc
index 2e9262eb1590d0e4cdf8d10e70e2f20395dae34f..2f2533aefd4726ae36ce74ababedd9de89c8cdce 100644
--- a/src/Deltat.cc
+++ b/src/Deltat.cc
@@ -51,14 +51,14 @@ field_deltat(const Field &field0, const Field &field1, Field &field2, double fac
 {
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
-  if (field1.nmiss || field0.nmiss)
+  if (field1.numMissVals || field0.numMissVals)
     {
       if (field1.memType == MemType::Float)
         varray_deltat(field1.size, field0.vec_f, field1.vec_f, field2.vec_f, factor, field1.missval);
       else
         varray_deltat(field1.size, field0.vec_d, field1.vec_d, field2.vec_d, factor, field1.missval);
 
-      field2.nmiss = field_num_mv(field2);
+      field2.numMissVals = field_num_mv(field2);
     }
   else
     {
@@ -69,8 +69,19 @@ field_deltat(const Field &field0, const Field &field1, Field &field2, double fac
     }
 }
 
-class ModuleDeltat
+class Deltat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Deltat",
+    .operators = { { "deltat", DeltatHelp }, { "timederivative", 0, 1, DeltatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Deltat> registration = RegisterEntry<Deltat>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -85,13 +96,10 @@ class ModuleDeltat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  cdo_operator_add("deltat",            0,     0, nullptr);
-  cdo_operator_add("timederivative",    0,     1, nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -174,17 +182,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Deltat(void *process)
-{
-  ModuleDeltat deltat;
-  deltat.init(process);
-  deltat.run();
-  deltat.close();
-
-  return nullptr;
-}
diff --git a/src/Deltime.cc b/src/Deltime.cc
index 4cefb5136c2d732845f145e510b9b6663c70e3fd..8369959cb2e4ee299d2bc7a95ee53060b3488e52 100644
--- a/src/Deltime.cc
+++ b/src/Deltime.cc
@@ -13,8 +13,20 @@
 
 constexpr const char *cmons[] = { "", "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
 
-class ModuleDeltime
+class Deltime : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Deltime",
+    .operators = { { "delday"}, { "del29feb"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Deltime> registration = RegisterEntry<Deltime>(module);
+  int DELDAY, DEL29FEB;
   int dday, dmon;
 
   CdoStreamID streamID1;
@@ -33,15 +45,14 @@ class ModuleDeltime
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     dataIsUnchanged = data_is_unchanged();
 
     // clang-format off
-  auto DELDAY   = cdo_operator_add("delday",   0, 0, nullptr);
-  auto DEL29FEB = cdo_operator_add("del29feb", 0, 0, nullptr);
+DELDAY = module.get_id("delday");
+DEL29FEB = module.get_id("del29feb");
     // clang-format on
 
     (void) (DELDAY);  // unused
@@ -149,19 +160,5 @@ public:
     if (nfound == 0) cdo_warning("Day %d%s not found!", dday, cmons[dmon]);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Deltime(void *process)
-{
-  ModuleDeltime deltime;
-
-  deltime.init(process);
-  deltime.run();
-  deltime.close();
-
-  return nullptr;
-}
diff --git a/src/Depth.cc b/src/Depth.cc
index a3f4de270f97caea2e9b703531c09f313635d039..6cf1bd3cc1fc2ec2b4eb1f821b30a5211f4b1621 100644
--- a/src/Depth.cc
+++ b/src/Depth.cc
@@ -23,7 +23,7 @@ static void
 calc_full_depth(size_t gridsize, size_t nlevels, const Varray<T> &thick_c, const Varray<T> &stretch_c, const Varray<T> &zos,
                 Varray<T> &fullDepth)
 {
-  varray_fill(gridsize, fullDepth, 0.0);
+  ranges::fill_n(fullDepth.begin(), gridsize, 0.0);
 
   for (size_t k = 1; k < nlevels; ++k)
     {
@@ -53,8 +53,20 @@ calc_full_depth(const Field3D &thick_c, const Field3D &stretch_c, const Field3D
     calc_full_depth(gridsize, nlevels, thick_c.vec_d, stretch_c.vec_d, zos.vec_d, fullDepth.vec_d);
 }
 
-class ModuleDepth
+class Depth : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Depth",
+    .operators = { { "zsdepth"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Depth> registration = RegisterEntry<Depth>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -70,12 +82,8 @@ class ModuleDepth
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("zsdepth", 0, 0, nullptr);
-
     streamID1 = cdo_open_read(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
@@ -94,10 +102,10 @@ public:
         auto varname = string_to_lower(varList1[varID].name);
 
         // clang-format off
-      if      (varname == "prism_thick_c") thickID = varID;
-      else if (varname == "stretch_c")     stretchID = varID;
-      else if (varname == "zos")           zosID = varID;
-      else if (varname == "draftave")      draftaveID = varID;
+        if      (varname == "prism_thick_c") thickID = varID;
+        else if (varname == "stretch_c")     stretchID = varID;
+        else if (varname == "zos")           zosID = varID;
+        else if (varname == "draftave")      draftaveID = varID;
         // clang-format on
       }
 
@@ -105,11 +113,10 @@ public:
       {
         cdo_print("Found:");
         // clang-format off
-
-      if (-1 != thickID)    cdo_print("  %s -> %s", "prism thickness at cells", varList1[thickID].name);
-      if (-1 != stretchID)  cdo_print("  %s -> %s", "zstar surface stretch at cell center", varList1[stretchID].name);
-      if (-1 != zosID)      cdo_print("  %s -> %s", "zstar sfc elevation at cell center", varList1[zosID].name);
-      if (-1 != draftaveID) cdo_print("  %s -> %s", "draftave", varList1[draftaveID].name);
+        if (-1 != thickID)    cdo_print("  %s -> %s", "prism thickness at cells", varList1[thickID].name);
+        if (-1 != stretchID)  cdo_print("  %s -> %s", "zstar surface stretch at cell center", varList1[stretchID].name);
+        if (-1 != zosID)      cdo_print("  %s -> %s", "zstar sfc elevation at cell center", varList1[zosID].name);
+        if (-1 != draftaveID) cdo_print("  %s -> %s", "draftave", varList1[draftaveID].name);
         // clang-format on
       }
 
@@ -146,6 +153,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -162,9 +170,9 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, vardata1[varID], levelID, &nmiss);
-            if (nmiss) cdo_abort("Missing values unsupported!");
+            size_t numMissVals;
+            cdo_read_record(streamID1, vardata1[varID], levelID, &numMissVals);
+            if (numMissVals) cdo_abort("Missing values unsupported!");
           }
 
         if (-1 != draftaveID) field2_add(vardata1[zosID], vardata1[draftaveID]);
@@ -179,23 +187,11 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Depth(void *process)
-{
-  ModuleDepth depth;
-  depth.init(process);
-  depth.run();
-  depth.close();
-  return nullptr;
-}
diff --git a/src/Derivepar.cc b/src/Derivepar.cc
index 33a3caa19a4add1383bc77709c992ad93bf333ad..6c9b8ec849740d501a9b48ec22503f213e4a6ae8 100644
--- a/src/Derivepar.cc
+++ b/src/Derivepar.cc
@@ -8,7 +8,8 @@
 /*
    This module contains the following operators:
 
-      Derivepar     gheight           Geopotential height
+      Derivepar     gheight           Geopotential height on full-levels
+      Derivepar     gheight_half      Geopotential height on half-levels
       Derivepar     sealevelpressure  Sea level pressure
 */
 
@@ -22,10 +23,8 @@
 #include "cdo_zaxis.h"
 #include "cdo_options.h"
 
-void geopot_height_halflevel(double *gheight, const double *const ta_fl, const double *const hus_fl, const double *const p_hl,
-                             long ngp, long nlev);
-void geopot_height_fulllevel(double *gheight, const double *const ta_fl, const double *const hus_fl, const double *const p_hl,
-                             long ngp, long nlev);
+void geopot_height_half(double *gheight, const double *ta_fl, const double *hus_fl, const double *p_hl, long ngp, long nlev);
+void geopot_height_full(double *gheight, const double *ta_fl, const double *hus_fl, const double *p_hl, long ngp, long nlev);
 
 static void
 check_range_var2d(int stepNum, const Varray<double> &var2d, double rMin, double rMax, const char *varname)
@@ -58,8 +57,21 @@ check_range_var3d(int stepNum, int nlevels, size_t gridsize, const Varray<double
     }
 }
 
-class ModuleDerivepar
+class Derivepar : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Derivepar",
+    .operators = { { "gheight", DeriveparHelp }, { "gheight_half", DeriveparHelp }, { "sealevelpressure", DeriveparHelp } },
+    .aliases = { { "gheight_full", "gheight" }, { "gheighthalf", "gheight_half" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Derivepar>(module);
+
+  int GHEIGHT_FULL, GHEIGHT_HALF, SEALEVELPRESSURE;
   int surfaceID = -1;
   int presID = -1;
 
@@ -73,35 +85,33 @@ class ModuleDerivepar
   size_t gridsize;
   VarIDs varIDs;
   int zaxisID_ML = -1;
-  int numHybridLevels = 0, numFullLevels = 0, numHalfLevels = 0;
+  int numHybridLevels = 0;
+  int numFullLevels = 0;
+  int numHalfLevels = 0;
 
   Varray<double> array;
   Varray<double> sgeopot;
   Varray<double> ps;
   Varray<double> temp;
 
-  Varray<double> hum, gheight;
+  Varray<double> hum;
+  Varray<double> gheight;
   Varray<double> halfPress;
-  Varray<double> fullPress, sealevelpressure;
+  Varray<double> fullPress;
+  Varray<double> sealevelpressure;
   Varray<double> vct;
 
   VarList varList1;
 
   int operatorID;
 
-  int GHEIGHT, GHEIGHTHALF, SEALEVELPRESSURE;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    GHEIGHT          = cdo_operator_add("gheight",            0, 0, nullptr);
-    GHEIGHTHALF      = cdo_operator_add("gheighthalf",        0, 0, nullptr);
-    SEALEVELPRESSURE = cdo_operator_add("sealevelpressure",   0, 0, nullptr);
-    // clang-format on
+    GHEIGHT_FULL = module.get_id("gheight");
+    GHEIGHT_HALF = module.get_id("gheight_half");
+    SEALEVELPRESSURE = module.get_id("sealevelpressure");
 
     operatorID = cdo_operator_id();
 
@@ -119,7 +129,12 @@ public:
     int vctSize = vct.size();
 
     if (Options::cdoVerbose)
-      for (int i = 0; i < vctSize / 2; ++i) cdo_print("vct: %5d %25.17f %25.17f", i, vct[i], vct[vctSize / 2 + i]);
+      {
+        auto numAB = vctSize / 2;
+        for (int i = 0; i < 10; ++i) cdo_print("vct: %5d %25.17f %25.17f", i, vct[i], vct[numAB + i]);
+        cdo_print("vct:   ...");
+        for (int i = numAB - 10; i < numAB; ++i) cdo_print("vct: %5d %25.17f %25.17f", i, vct[i], vct[numAB + i]);
+      }
 
     if (zaxisID_ML == -1) cdo_abort("No 3D variable with hybrid sigma pressure coordinate found!");
 
@@ -134,13 +149,13 @@ public:
       {
         cdo_print("Found:");
         // clang-format off
-      if (-1 != varIDs.humID)     cdo_print("  %s -> %s", var_stdname(specific_humidity), varList1[varIDs.humID].name);
-      if (-1 != varIDs.tempID)    cdo_print("  %s -> %s", var_stdname(air_temperature), varList1[varIDs.tempID].name);
-      if (-1 != varIDs.psID)      cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1[varIDs.psID].name);
-      if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), varList1[varIDs.lnpsID].name);
-      if (-1 != varIDs.sgeopotID) cdo_print("  %s -> %s", var_stdname(surface_geopotential), varList1[varIDs.sgeopotID].name);
-      if (-1 != varIDs.geopotID)  cdo_print("  %s -> %s", var_stdname(geopotential), varList1[varIDs.geopotID].name);
-      if (-1 != varIDs.gheightID) cdo_print("  %s -> %s", var_stdname(geopotential_height), varList1[varIDs.gheightID].name);
+        if (-1 != varIDs.humID)     cdo_print("  %s -> %s", var_stdname(specific_humidity), varList1[varIDs.humID].name);
+        if (-1 != varIDs.tempID)    cdo_print("  %s -> %s", var_stdname(air_temperature), varList1[varIDs.tempID].name);
+        if (-1 != varIDs.psID)      cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1[varIDs.psID].name);
+        if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), varList1[varIDs.lnpsID].name);
+        if (-1 != varIDs.sgeopotID) cdo_print("  %s -> %s", var_stdname(surface_geopotential), varList1[varIDs.sgeopotID].name);
+        if (-1 != varIDs.geopotID)  cdo_print("  %s -> %s", var_stdname(geopotential), varList1[varIDs.geopotID].name);
+        if (-1 != varIDs.gheightID) cdo_print("  %s -> %s", var_stdname(geopotential_height), varList1[varIDs.gheightID].name);
         // clang-format on
       }
 
@@ -151,22 +166,22 @@ public:
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        auto gridID = varList1[varID].gridID;
-        auto zaxisID = varList1[varID].zaxisID;
-
         if (operatorID == SEALEVELPRESSURE) varIDs.humID = -1;
 
-        if (gridInqType(gridID) == GRID_SPECTRAL && zaxisInqType(zaxisID) == ZAXIS_HYBRID)
+        const auto &var = varList1[varID];
+        if (var.gridType == GRID_SPECTRAL && var.zaxisType == ZAXIS_HYBRID)
           cdo_abort("Spectral data on model level unsupported!");
 
-        if (gridInqType(gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
+        if (var.gridType == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
       }
+
     array = Varray<double>(gridsize);
     sgeopot = Varray<double>(gridsize);
     ps = Varray<double>(gridsize);
     temp = Varray<double>(gridsize * numFullLevels);
     halfPress = Varray<double>(gridsize * (numFullLevels + 1));
-    if (operatorID == GHEIGHT || operatorID == GHEIGHTHALF)
+
+    if (operatorID == GHEIGHT_FULL || operatorID == GHEIGHT_HALF)
       {
         if (varIDs.humID == -1)
           cdo_warning("%s not found - using algorithm without %s!", var_stdname(specific_humidity), var_stdname(specific_humidity));
@@ -191,7 +206,7 @@ public:
         else
           cdo_print("%s not found - using bottom layer of %s!", var_stdname(surface_geopotential), var_stdname(geopotential));
 
-        varray_fill(sgeopot, 0.0);
+        ranges::fill(sgeopot, 0.0);
       }
 
     presID = varIDs.lnpsID;
@@ -218,12 +233,12 @@ public:
       int var_id = -1;
       int varID = -1;
 
-      if (operatorID == GHEIGHT)
+      if (operatorID == GHEIGHT_FULL)
         {
           var_id = geopotential_height;
           varID = vlistDefVar(vlistID2, gridID0, zaxisID_ML, TIME_VARYING);
         }
-      else if (operatorID == GHEIGHTHALF)
+      else if (operatorID == GHEIGHT_HALF)
         {
           auto zaxisID_ML_Half = zaxisCreate(ZAXIS_HYBRID_HALF, numHalfLevels);
           zaxisDefVct(zaxisID_ML_Half, 2 * numHalfLevels, vct.data());
@@ -254,6 +269,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -270,8 +286,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             auto offset = gridsize * levelID;
 
@@ -309,28 +325,17 @@ public:
 
         if (varIDs.humID != -1) check_range_var3d(tsID + 1, varList1[varIDs.humID].nlevels, gridsize, hum, -0.1, MAX_Q, "Humidity");
 
-        if (operatorID == GHEIGHT || operatorID == GHEIGHTHALF)
+        if (operatorID == GHEIGHT_FULL || operatorID == GHEIGHT_HALF)
           {
             vct_to_hybrid_pressure((double *) nullptr, halfPress.data(), vct.data(), ps.data(), numFullLevels, gridsize);
             array_copy(gridsize, sgeopot.data(), gheight.data() + gridsize * numFullLevels);
-            if (operatorID == GHEIGHT)
-              geopot_height_fulllevel(gheight.data(), temp.data(), hum.data(), halfPress.data(), gridsize, numFullLevels);
+            if (operatorID == GHEIGHT_FULL)
+              geopot_height_full(gheight.data(), temp.data(), hum.data(), halfPress.data(), gridsize, numFullLevels);
             else
-              geopot_height_halflevel(gheight.data(), temp.data(), hum.data(), halfPress.data(), gridsize, numFullLevels);
-
-            int varID = 0;
-            auto nlevels = (operatorID == GHEIGHT) ? numFullLevels : numHalfLevels;
-            for (int levelID = 0; levelID < nlevels; ++levelID)
-              {
-                cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, gheight.data() + levelID * gridsize, 0);
-              }
-          }
-        else if (operatorID == GHEIGHTHALF)
-          {
+              geopot_height_half(gheight.data(), temp.data(), hum.data(), halfPress.data(), gridsize, numFullLevels);
 
             int varID = 0;
-            auto nlevels = numHalfLevels;
+            auto nlevels = (operatorID == GHEIGHT_FULL) ? numFullLevels : numHalfLevels;
             for (int levelID = 0; levelID < nlevels; ++levelID)
               {
                 cdo_def_record(streamID2, varID, levelID);
@@ -341,9 +346,8 @@ public:
           {
             vct_to_hybrid_pressure(fullPress.data(), halfPress.data(), vct.data(), ps.data(), numFullLevels, gridsize);
 
-            extrapolate_P(sealevelpressure.data(), &halfPress[gridsize * (numFullLevels)],
-                          &fullPress[gridsize * (numFullLevels - 1)], sgeopot.data(), &temp[gridsize * (numFullLevels - 1)],
-                          gridsize);
+            extrapolate_P(sealevelpressure.data(), &halfPress[gridsize * numFullLevels], &fullPress[gridsize * (numFullLevels - 1)],
+                          sgeopot.data(), &temp[gridsize * (numFullLevels - 1)], gridsize);
 
             cdo_def_record(streamID2, 0, 0);
             cdo_write_record(streamID2, sealevelpressure.data(), 0);
@@ -354,25 +358,13 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Derivepar(void *process)
-{
-  ModuleDerivepar derivepar;
-  derivepar.init(process);
-  derivepar.run();
-  derivepar.close();
-  return nullptr;
-}
diff --git a/src/Detrend.cc b/src/Detrend.cc
index 483e14e7b0149f4641dbcfac322deb3b544fda47..e2bf9f6c01aad82f129eccc45f561d0b901dc2ee 100644
--- a/src/Detrend.cc
+++ b/src/Detrend.cc
@@ -57,10 +57,7 @@ detrend(long nts, const Varray<double> &deltaTS0, double missval1, const Varray<
     work2 = SUBM(DIVM(sumx, n), MULM(DIVM(sumj, n), work1));
   }
 
-  auto detrend_kernel = [&](auto j, auto is_EQ)
-    {
-      return SUBM(array1[j], ADDM(work2, MULM(work1, deltaTS0[j])));
-    };
+  auto detrend_kernel = [&](auto j, auto is_EQ) { return SUBM(array1[j], ADDM(work2, MULM(work1, deltaTS0[j]))); };
 
   if (std::isnan(missval1))
     for (long j = 0; j < nts; ++j) array2[j] = detrend_kernel(j, dbl_is_equal);
@@ -93,7 +90,7 @@ detrendGetParameter(bool &tstepIsEqual)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -111,8 +108,20 @@ detrendGetParameter(bool &tstepIsEqual)
     }
 }
 
-class ModuleDetrend
+class Detrend : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Detrend",
+    .operators = { { "detrend", DetrendHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Detrend> registration = RegisterEntry<Detrend>(module);
+
   int varID, levelID;
   DateTimeList dtlist;
 
@@ -131,10 +140,8 @@ class ModuleDetrend
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     detrendGetParameter(tstepIsEqual);
 
     streamID1 = cdo_open_read(0);
@@ -250,18 +257,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Detrend(void *process)
-{
-  ModuleDetrend detrend;
-  detrend.init(process);
-  detrend.run();
-  detrend.close();
-
-  return nullptr;
-}
diff --git a/src/Diff.cc b/src/Diff.cc
index 3ff274dca6c3af8695e60354cbe93a9934753786..fc14ca99dd1f0064c93c4a1c7bda710b2ea31cff 100644
--- a/src/Diff.cc
+++ b/src/Diff.cc
@@ -75,7 +75,7 @@ static DiffParam
 diff(size_t gridsize, const Field &field1, const Field &field2)
 {
   DiffParam diffParam;
-  auto hasMissvals = (field1.nmiss || field2.nmiss);
+  auto hasMissvals = (field1.numMissVals || field2.numMissVals);
   if (hasMissvals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
@@ -141,7 +141,7 @@ diff_get_parameter(double &abslim, double &abslim2, double &rellim, int &mapflag
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -169,8 +169,24 @@ diff_get_parameter(double &abslim, double &abslim2, double &rellim, int &mapflag
     }
 }
 
-class ModuleDiff
+class Diff : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Diff",
+    .operators = { { "diff", DiffHelp },
+                   { "diffp", DiffHelp },
+                   { "diffn", DiffHelp },
+                   { "diffc", DiffHelp } },
+    .aliases = { { "diffv", "diffn" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 2, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Diff> registration = RegisterEntry<Diff>(module);
+
+  int DIFF, DIFFP, DIFFN, DIFFC;
   bool printHeader = true;
   int varID1, varID2 = -1;
   int levelID;
@@ -188,21 +204,14 @@ class ModuleDiff
   std::map<int, int> mapOfVarIDs;
   VarList varList1, varList2;
 
-  Field field1, field2;
-
-  int DIFF, DIFFP, DIFFN, DIFFC;
-
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    DIFF = cdo_operator_add("diff", 0, 0, nullptr);
-    DIFFP = cdo_operator_add("diffp", 0, 0, nullptr);
-    DIFFN = cdo_operator_add("diffn", 0, 0, nullptr);
-    DIFFC = cdo_operator_add("diffc", 0, 0, nullptr);
+    DIFF = module.get_id("diff");
+    DIFFP = module.get_id("diffp");
+    DIFFN = module.get_id("diffn");
+    DIFFC = module.get_id("diffc");
 
     operatorID = cdo_operator_id();
 
@@ -236,6 +245,7 @@ public:
   void
   run()
   {
+    Field field1, field2;
 
     int nrecs, nrecs2;
     int indg = 0;
@@ -282,22 +292,23 @@ public:
 
             indg += 1;
 
-            auto gridsize = varList1[varID1].gridsize;
+            const auto &var1 = varList1[varID1];
+            const auto &var2 = varList2[varID2];
 
             // checkrel = gridInqType(gridID) != GRID_SPECTRAL;
             auto checkrel = true;
 
-            cdiParamToString(varList1[varID1].param, paramstr, sizeof(paramstr));
+            cdiParamToString(var1.param, paramstr, sizeof(paramstr));
 
-            field1.init(varList1[varID1]);
+            field1.init(var1);
             cdo_read_record(streamID1, field1);
-            if (varList1[varID1].nwpv == CDI_COMP) use_real_part(gridsize, field1);
+            if (var1.nwpv == CDI_COMP) use_real_part(var1.gridsize, field1);
 
-            field2.init(varList2[varID2]);
+            field2.init(var2);
             cdo_read_record(streamID2, field2);
-            if (varList2[varID2].nwpv == CDI_COMP) use_real_part(gridsize, field2);
+            if (var2.nwpv == CDI_COMP) use_real_part(var1.gridsize, field2);
 
-            auto dp = diff(gridsize, field1, field2);
+            auto dp = diff(var1.gridsize, field1, field2);
 
             if (!Options::silentMode || Options::cdoVerbose)
               {
@@ -328,8 +339,8 @@ public:
                     fprintf(stdout, "%s %s ", vdateString.c_str(), vtimeString.c_str());
                     reset_text_color(stdout);
                     set_text_color(stdout, GREEN);
-                    fprintf(stdout, "%7g ", cdo_zaxis_inq_level(varList1[varID1].zaxisID, levelID));
-                    fprintf(stdout, "%8zu %7zu ", gridsize, std::max(field1.nmiss, field2.nmiss));
+                    fprintf(stdout, "%7g ", cdo_zaxis_inq_level(var1.zaxisID, levelID));
+                    fprintf(stdout, "%8zu %7zu ", var1.gridsize, std::max(field1.numMissVals, field2.numMissVals));
                     fprintf(stdout, "%7zu ", dp.ndiff);
                     reset_text_color(stdout);
 
@@ -342,11 +353,11 @@ public:
 
                     set_text_color(stdout, BRIGHT, GREEN);
                     if (operatorID == DIFFN)
-                      fprintf(stdout, "%-11s", varList1[varID1].name.c_str());
+                      fprintf(stdout, "%-11s", var1.name.c_str());
                     else if (operatorID == DIFF || operatorID == DIFFP)
                       fprintf(stdout, "%-11s", paramstr);
                     else if (operatorID == DIFFC)
-                      fprintf(stdout, "%4d", varList1[varID1].code);
+                      fprintf(stdout, "%4d", var1.code);
                     reset_text_color(stdout);
 
                     fprintf(stdout, "\n");
@@ -392,18 +403,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Diff(void *process)
-{
-  ModuleDiff diff;
-  diff.init(process);
-  diff.run();
-  diff.close();
-
-  return nullptr;
-}
diff --git a/src/Distgrid.cc b/src/Distgrid.cc
index 3c231457c9ae7a2f853043122c9ae8c6514e91a4..7ad8d106a8933c70adf234af58b73c89cab57b98 100644
--- a/src/Distgrid.cc
+++ b/src/Distgrid.cc
@@ -334,8 +334,19 @@ dist_cells(const Field &field1, Field &field2, const DistgridInfo &distgridInfo)
     for (size_t i = 0; i < gridsize; ++i) field2.vec_d[i] = field1.vec_d[cellidx[i]];
 }
 
-class ModuleDistgrid
+class Distgrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Distgrid",
+    .operators = { { "distgrid", DistgridHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Distgrid> registration = RegisterEntry<Distgrid>(module);
   size_t MaxBlocks = 99999;
   int gridID1;
   int gridtype = -1;
@@ -354,12 +365,8 @@ class ModuleDistgrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
-
     operator_input_arg("nxblocks, [nyblocks]");
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
     if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
@@ -471,7 +478,7 @@ public:
 
     for (size_t index = 0; index < nsplit; ++index)
       {
-        auto fileName = cdo_get_obase() + string_format("%05ld", (long)index);
+        auto fileName = cdo_get_obase() + string_format("%05ld", (long) index);
         if (fileSuffix.size() > 0) fileName += fileSuffix;
 
         streamIDs[index] = cdo_open_write(fileName.c_str());
@@ -525,7 +532,7 @@ public:
                 else
                   dist_cells(field1, field2, distgridInfo[0][index]);
 
-                if (field1.nmiss) field_num_mv(field2);
+                if (field1.numMissVals) field_num_mv(field2);
 
                 cdo_def_record(streamIDs[index], varID, levelID);
                 cdo_write_record(streamIDs[index], field2);
@@ -546,18 +553,5 @@ public:
 
     for (int i = 0; i < ngrids; ++i)
       for (size_t index = 0; index < nsplit; ++index) gridDestroy(distgridInfo[i][index].gridID);
-
-    cdo_finish();
   }
 };
-
-void *
-Distgrid(void *process)
-{
-  ModuleDistgrid distrid;
-  distrid.init(process);
-  distrid.run();
-  distrid.close();
-
-  return nullptr;
-}
diff --git a/src/Duplicate.cc b/src/Duplicate.cc
index 4d1d835fee86d66737dd158f9d47962b1100cb35..0c26b8ba2190ecf586571006fbb19878d9d9139b 100644
--- a/src/Duplicate.cc
+++ b/src/Duplicate.cc
@@ -13,8 +13,19 @@
 #include "param_conversion.h"
 #include "field_functions.h"
 
-class ModuleDuplicate
+class Duplicate : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Duplicate",
+    .operators = { { "duplicate", DuplicateHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Duplicate> registration = RegisterEntry<Duplicate>(module);
   FieldVector3D vars;
   std::vector<CdiDateTime> vDateTimes;
 
@@ -32,9 +43,8 @@ class ModuleDuplicate
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     if (cdo_operator_argc() > 1) cdo_abort("Too many arguments!");
 
@@ -132,17 +142,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Duplicate(void *process)
-{
-  ModuleDuplicate duplicate;
-  duplicate.init(process);
-  duplicate.run();
-  duplicate.close();
-
-  return nullptr;
-}
diff --git a/src/EOFs.cc b/src/EOFs.cc
index be4f982dc546a255e6fecd09fa938a2464d96a74..49e0a841fb688935a7818e73dcf8387ff8017ec6 100644
--- a/src/EOFs.cc
+++ b/src/EOFs.cc
@@ -95,7 +95,7 @@ scale_eigvec_time(Varray<double> &out, int tsID, int nts, size_t npack, const st
     }
 }
 
-class ModuleEOFs
+class EOFs : public Process
 {
   enum
   {
@@ -104,6 +104,20 @@ class ModuleEOFs
     EOF_SPATIAL
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "EOFs",
+    .operators = { { "eof", EOF_, 0, EOFsHelp },
+                   { "eofspatial", EOF_SPATIAL, 0, EOFsHelp },
+                   { "eoftime", EOF_TIME, 0, EOFsHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 2, OnlyFirst },
+  };
+  inline static RegisterEntry<EOFs> registration = RegisterEntry<EOFs>(module);
+
   struct eofdata_t
   {
     bool init = false;
@@ -113,7 +127,7 @@ class ModuleEOFs
     Varray2D<double> data;
   };
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   int varID, levelID;
   int nts = 1;
   int grid_space = 0, time_space = 0;
@@ -152,14 +166,10 @@ class ModuleEOFs
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  cdo_operator_add("eof",        EOF_,        0, nullptr);
-  cdo_operator_add("eoftime",    EOF_TIME,    0, nullptr);
-  cdo_operator_add("eofspatial", EOF_SPATIAL, 0, nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -302,7 +312,7 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, in.data(), &nmiss);
+            cdo_read_record(streamID1, in.data(), &numMissVals);
 
             auto gridsize = varList1[varID].gridsize;
             auto missval = varList1[varID].missval;
@@ -520,17 +530,17 @@ public:
                     else if (time_space)
                       scale_eigvec_time(out, tsID, nts, npack, pack, weights, covar, data, missval, sum_w);
 
-                    nmiss = varray_num_mv(gridsize, out, missval);
+                    numMissVals = varray_num_mv(gridsize, out, missval);
                     cdo_def_record(streamID3, varID, levelID);
-                    cdo_write_record(streamID3, out.data(), nmiss);
+                    cdo_write_record(streamID3, out.data(), numMissVals);
                   }  // loop n_eig
 
                 auto eig_val = eofdata[varID][levelID].eig_val.data();
 
-                nmiss = (DBL_IS_EQUAL(eig_val[tsID], missval)) ? 1 : 0;
+                numMissVals = (DBL_IS_EQUAL(eig_val[tsID], missval)) ? 1 : 0;
 
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, &eig_val[tsID], nmiss);
+                cdo_write_record(streamID2, &eig_val[tsID], numMissVals);
               }  // loop nlevs
           }      // loop nvars
       }
@@ -549,18 +559,5 @@ public:
 
     //  taxisDestroy(taxisID2);
     //  taxisDestroy(taxisID3);
-    cdo_finish();
   }
 };
-
-void *
-EOFs(void *process)
-{
-  ModuleEOFs eofs;
-
-  eofs.init(process);
-  eofs.run();
-  eofs.close();
-
-  return nullptr;
-}
diff --git a/src/EcaEtccdi.cc b/src/EcaEtccdi.cc
index 8e41b29981c967cfe7a97d246486a43bcd96af32..401b7eb1a2e043e1d4686a8792ac7b337d109f2b 100644
--- a/src/EcaEtccdi.cc
+++ b/src/EcaEtccdi.cc
@@ -111,7 +111,7 @@ writeTimesteps(int MaxMonths, int recentYear, FieldVector3D &cei, int frequency,
 
               fieldc_div(cei[loopmonth - 2][0][levelIDo], (double) (tempdpm[loopmonth - 2] / 100.0));
               cdo_def_record(streamID4, varIDo, levelIDo);
-              cdo_write_record(streamID4, cei[loopmonth - 2][0][levelIDo].vec_d.data(), cei[loopmonth - 2][0][levelIDo].nmiss);
+              cdo_write_record(streamID4, cei[loopmonth - 2][0][levelIDo].vec_d.data(), cei[loopmonth - 2][0][levelIDo].numMissVals);
             }
           (*otsID)++;
         }
@@ -127,7 +127,7 @@ writeTimesteps(int MaxMonths, int recentYear, FieldVector3D &cei, int frequency,
 
           if (func2 == FieldFunc_Avg) fieldc_div(cei[0][0][levelIDo], (double) (tempdpy / 100.0));
           cdo_def_record(streamID4, varIDo, levelIDo);
-          cdo_write_record(streamID4, cei[0][0][levelIDo].vec_d.data(), cei[0][0][levelIDo].nmiss);
+          cdo_write_record(streamID4, cei[0][0][levelIDo].vec_d.data(), cei[0][0][levelIDo].numMissVals);
         }
       (*otsID)++;
     }
@@ -189,7 +189,7 @@ calculateOuterPeriod(Field &field, int MaxMonths, int recentYear, int endOfCalc,
       for (int recID = 0; recID < nrecs; ++recID)
         {
           streamInqRecord(cdiStream, &varID, &levelID);
-          streamReadRecord(cdiStream, field.vec_d.data(), &field.nmiss);
+          streamReadRecord(cdiStream, field.vec_d.data(), &field.numMissVals);
 
           Field &pctls = varsPtemp[dayOfYear][0][levelID];
           if (selection == func_selle)
@@ -364,7 +364,7 @@ etccdi_op(ETCCDI_REQUEST &request)
           int varID, levelID;
           cdo_inq_record(streamID2, &varID, &levelID);
           cdo_read_record(streamID2, vars2[dayOfYear][varID][levelID]);
-          varsPtemp[dayOfYear][varID][levelID].nmiss = vars2[dayOfYear][varID][levelID].nmiss;
+          varsPtemp[dayOfYear][varID][levelID].numMissVals = vars2[dayOfYear][varID][levelID].numMissVals;
         }
 
       for (int recID = 0; recID < nrecs; ++recID)
@@ -705,7 +705,7 @@ etccdi_op(ETCCDI_REQUEST &request)
                       cdo_def_record(streamID4, varIDo, levelIDo);
                       cdo_write_record(
                           streamID4, cei[(bootsyear - request.startboot) * MaxMonths + (month - 2)][varIDo][levelIDo].vec_d.data(),
-                          cei[(bootsyear - request.startboot) * MaxMonths + (month - 2)][varIDo][levelIDo].nmiss);
+                          cei[(bootsyear - request.startboot) * MaxMonths + (month - 2)][varIDo][levelIDo].numMissVals);
                     }
                   otsID++;
                 }
@@ -722,7 +722,7 @@ etccdi_op(ETCCDI_REQUEST &request)
 
                   cdo_def_record(streamID4, varIDo, levelIDo);
                   cdo_write_record(streamID4, cei[(bootsyear - request.startboot) * MaxMonths][varIDo][levelIDo].vec_d.data(),
-                                   cei[(bootsyear - request.startboot) * MaxMonths][varIDo][levelIDo].nmiss);
+                                   cei[(bootsyear - request.startboot) * MaxMonths][varIDo][levelIDo].numMissVals);
                 }
               otsID++;
             }
@@ -784,30 +784,42 @@ etccdi_op(ETCCDI_REQUEST &request)
   cdo_stream_close(streamID1);
 }
 
-class ModuleEcaEtccdi
+class EcaEtccdi : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "EcaEtccdi",
+    .operators = { { "etccdi_tx90p", func_selge, CMP_DATE, EcaEtccdiHelp },
+                   { "etccdi_tx10p", func_selle, CMP_DATE, EcaEtccdiHelp },
+                   { "etccdi_tn90p", func_selge, CMP_DATE, EcaEtccdiHelp },
+                   { "etccdi_tn10p", func_selle, CMP_DATE, EcaEtccdiHelp },
+                   { "etccdi_r95p", func_selge, CMP_DATE, EcaEtccdiHelp },
+                   { "etccdi_r99p", func_selge, CMP_DATE, EcaEtccdiHelp },
+                   { "etccdi", 0, CMP_DATE, EcaEtccdiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, FilesOnly },
+  };
+  inline static RegisterEntry<EcaEtccdi> registration = RegisterEntry<EcaEtccdi>(module);
+  int ETCCDI_TX90P, ETCCDI_R99P, ETCCDI_R95P, ETCCDI_TX10P, ETCCDI_TN90P, ETCCDI_TN10P;
   ETCCDI_REQUEST request;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     if (cdo_operator_argc() < 3) cdo_abort("Too few arguments!");
     if (cdo_operator_argc() > 4) cdo_abort("Too many arguments!");
 
-    int ETCCDI_TX90P, ETCCDI_R99P, ETCCDI_R95P, ETCCDI_TX10P, ETCCDI_TN90P, ETCCDI_TN10P;
-
-    ETCCDI_TX90P = cdo_operator_add("etccdi_tx90p", func_selge, CMP_DATE, nullptr);
-    ETCCDI_R99P = cdo_operator_add("etccdi_r99p", func_selge, CMP_DATE, nullptr);
-    ETCCDI_R95P = cdo_operator_add("etccdi_r95p", func_selge, CMP_DATE, nullptr);
-    ETCCDI_TX10P = cdo_operator_add("etccdi_tx10p", func_selle, CMP_DATE, nullptr);
-    ETCCDI_TN90P = cdo_operator_add("etccdi_tn90p", func_selge, CMP_DATE, nullptr);
-    ETCCDI_TN10P = cdo_operator_add("etccdi_tn10p", func_selle, CMP_DATE, nullptr);
-    cdo_operator_add("etccdi", 0, CMP_DATE, nullptr);
-
-    
+    ETCCDI_TX90P = module.get_id("etccdi_tx90p");
+    ETCCDI_R99P = module.get_id("etccdi_r99p");
+    ETCCDI_R95P = module.get_id("etccdi_r95p");
+    ETCCDI_TX10P = module.get_id("etccdi_tx10p");
+    ETCCDI_TN90P = module.get_id("etccdi_tn90p");
+    ETCCDI_TN10P = module.get_id("etccdi_tn10p");
 
     request.ndates = parameter_to_int(cdo_operator_argv(0));
     request.startboot = parameter_to_int(cdo_operator_argv(1));
@@ -896,17 +908,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-EcaEtccdi(void *process)
-{
-  ModuleEcaEtccdi ecaEtccdi;
-  ecaEtccdi.init(process);
-  ecaEtccdi.run();
-  ecaEtccdi.close();
-
-  return nullptr;
-}
diff --git a/src/EcaIndices.cc b/src/EcaIndices.cc
index f2473967cbd8705ca0fddab53789e192bae56943..a034e378b6468821294011274d3a6450e6b70874 100644
--- a/src/EcaIndices.cc
+++ b/src/EcaIndices.cc
@@ -318,7 +318,6 @@ static const char HURR_LONGNAME2[]   = "Greatest number of consecutive hurricane
 
 /* ECA temperature indices */
 
-
 static void
 set_default_compare_type(int &compare_type)
 {
@@ -329,7 +328,7 @@ static void
 set_compare_type_from_params(int &compare_type, const std::vector<std::string> &params)
 {
   KVList kvlist;
-  if (kvlist.parse_arguments(1, params) != 0) cdo_abort("Argument parse error!");
+  if (kvlist.parse_arguments(params) != 0) cdo_abort("Argument parse error!");
   auto kv = kvlist.search("freq");
   if (kv && kv->nvalues > 0)
     {
@@ -345,55 +344,86 @@ set_compare_type_from_params(int &compare_type, const std::vector<std::string> &
 template <typename Request>
 class EcaIndices : public Process
 {
+
 protected:
-  EcaIndices(std::function<void(Request p_request)> p_eca_func) : eca_func(p_eca_func) {}
+  EcaIndices(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module,
+             std::function<void(Request p_request)> p_eca_func)
+      : Process(p_ID, p_operatorName, p_arguments, p_module), eca_func(p_eca_func)
+  {
+  }
   std::function<void(Request p_request)> eca_func;
   Request request;
-  virtual void init(void *process) = 0;
+  virtual void init() = 0;
 
 public:
-  // clang-format off
-  void run() {
+  void
+  run()
+  {
     assert(request.compare_type != -1);
-    eca_func(request); }
-  void close() { cdo_finish(); }
-  // clang-format on
+    eca_func(request);
+  }
+
+  void
+  close()
+  {
+  }
 };
 
 class EcaIndices1 : public EcaIndices<ECA_REQUEST_1>
 {
 public:
-  EcaIndices1() : EcaIndices<ECA_REQUEST_1>(eca1) {}
+  EcaIndices1(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module)
+      : EcaIndices<ECA_REQUEST_1>(p_ID, p_operatorName, p_arguments, p_module, eca1)
+  {
+  }
 };
 
 class EcaIndices2 : public EcaIndices<ECA_REQUEST_2>
 {
 public:
-  EcaIndices2() : EcaIndices<ECA_REQUEST_2>(eca2) {}
+  EcaIndices2(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module)
+      : EcaIndices<ECA_REQUEST_2>(p_ID, p_operatorName, p_arguments, p_module, eca2)
+  {
+  }
 };
 
 class EcaIndices3 : public EcaIndices<ECA_REQUEST_3>
 {
 public:
-  EcaIndices3() : EcaIndices<ECA_REQUEST_3>(eca3) {}
+  EcaIndices3(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module)
+      : EcaIndices<ECA_REQUEST_3>(p_ID, p_operatorName, p_arguments, p_module, eca3)
+  {
+  }
 };
 
 class EcaIndices4 : public EcaIndices<ECA_REQUEST_4>
 {
 public:
-  EcaIndices4() : EcaIndices<ECA_REQUEST_4>(eca4) {}
+  EcaIndices4(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module)
+      : EcaIndices<ECA_REQUEST_4>(p_ID, p_operatorName, p_arguments, p_module, eca4)
+  {
+  }
 };
 
-class ModuleEcaCfd : public EcaIndices1
+class EcaCfd : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaCfd",
+    .operators = { { "eca_cfd", 0, CMP_DATE, EcaCfdHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaCfd> registration = RegisterEntry<EcaCfd>(module);
   int ndays = 5;
 
 public:
   void
-  init(void *process) override
+  init() override
   {
-    cdo_initialize(process);
-    cdo_operator_add("eca_cfd", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
@@ -410,8 +440,8 @@ public:
 
     char cfd_longname2[1024];
     char cfd_name2[1024];
-    sprintf(cfd_longname2, CFD_LONGNAME2, ndays);
-    sprintf(cfd_name2, CFD_NAME2, ndays);
+    std::snprintf(cfd_longname2, sizeof(cfd_longname2), CFD_LONGNAME2, ndays);
+    std::snprintf(cfd_name2, sizeof(cfd_name2), CFD_NAME2, ndays);
 
     request.var1.name = CFD_NAME;
     request.var1.longname = CFD_LONGNAME;
@@ -429,27 +459,26 @@ public:
   }
 };
 
-void *
-EcaCfd(void *process)
-{
-  ModuleEcaCfd ecacfg;
-  ecacfg.init(process);
-  ecacfg.run();
-  ecacfg.close();
-  return nullptr;
-}
-
-class ModuleEcaCsu : public EcaIndices1
+class EcaCsu : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaCsu",
+    .operators = { { "eca_csu", 0, CMP_DATE, EcaCsuHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaCsu> registration = RegisterEntry<EcaCsu>(module);
   double argT = 25.0;
   int ndays = 5;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("eca_csu", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 3) cdo_abort("Too many arguments!");
@@ -467,8 +496,8 @@ public:
 
     char csu_longname2[1024];
     char csu_name2[1024];
-    sprintf(csu_longname2, CSU_LONGNAME2, ndays);
-    sprintf(csu_name2, CSU_NAME2, ndays);
+    std::snprintf(csu_longname2, sizeof(csu_longname2), CSU_LONGNAME2, ndays);
+    std::snprintf(csu_name2, sizeof(csu_name2), CSU_NAME2, ndays);
 
     request.var1.name = CSU_NAME;
     request.var1.longname = CSU_LONGNAME;
@@ -486,28 +515,27 @@ public:
   }
 };
 
-void *
-EcaCsu(void *process)
-{
-  ModuleEcaCsu ecacsu;
-  ecacsu.init(process);
-  ecacsu.run();
-  ecacsu.close();
-  return nullptr;
-}
-
-class ModuleEcaCwdi : public EcaIndices2
+class EcaCwdi : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaCwdi",
+    .operators = { { "eca_cwdi", 0, CMP_DATE, EcaCwdiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaCwdi> registration = RegisterEntry<EcaCwdi>(module);
   int argN = 6;
   double argT = 5.0;
   char longname[sizeof(CWDI_LONGNAME) + 80];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("eca_cwdi", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 2)
@@ -526,7 +554,7 @@ public:
           argN = parameter_to_int(cdo_operator_argv(0));
       }
 
-    sprintf(longname, CWDI_LONGNAME, argN, argT);
+    std::snprintf(longname, sizeof(longname), CWDI_LONGNAME, argN, argT);
 
     request.var1.name = CWDI_NAME;
     request.var1.longname = longname;
@@ -547,29 +575,29 @@ public:
   }
 };
 
-void *
-EcaCwdi(void *process)
-{
-  ModuleEcaCwdi ecacsu;
-  ecacsu.init(process);
-  ecacsu.run();
-  ecacsu.close();
-  return nullptr;
-}
-
-class ModuleEcaCwfi : public EcaIndices2
+class EcaCwfi : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaCwfi",
+    .operators = { { "eca_cwfi", 0, CMP_DATE, EcaCwfiHelp }, { "etccdi_csdi", 0, CMP_YEAR, EcaCwfiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaCwfi> registration = RegisterEntry<EcaCwfi>(module);
+  int ECA_CWFI, ETCCDI_CSDI;
   int argN = 6;
   char longname[sizeof(CWFI_LONGNAME) + 40];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    int ECA_CWFI, ETCCDI_CSDI;
-    ECA_CWFI = cdo_operator_add("eca_cwfi", 0, CMP_DATE, nullptr);
-    ETCCDI_CSDI = cdo_operator_add("etccdi_csdi", 0, CMP_YEAR, nullptr);
+    ECA_CWFI = module.get_id("eca_cwfi");
+    ETCCDI_CSDI = module.get_id("etccdi_csdi");
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 1)
@@ -586,7 +614,7 @@ public:
 
     if (ECA_CWFI == cdo_operator_id())
       {
-        sprintf(longname, CWFI_LONGNAME, argN);
+        std::snprintf(longname, sizeof(longname), CWFI_LONGNAME, argN);
         request.var1.name = CWFI_NAME;
         request.var1.longname = longname;
         request.var1.units = CWFI_UNITS;
@@ -613,25 +641,25 @@ public:
   }
 };
 
-void *
-EcaCwfi(void *process)
+class EcaEtr : public EcaIndices3
 {
-  ModuleEcaCwfi eca;
-  eca.init(process);
-  eca.run();
-  eca.close();
-  return nullptr;
-}
+public:
+  using EcaIndices3::EcaIndices3;
+  inline static CdoModule module = {
+    .name = "EcaEtr",
+    .operators = { { "eca_etr", 0, CMP_DATE, EcaEtrHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaEtr> registration = RegisterEntry<EcaEtr>(module);
 
-class ModuleEcaEtr : public EcaIndices3
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_etr", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.name = ETR_NAME;
@@ -643,26 +671,27 @@ public:
   }
 };
 
-void *
-EcaEtr(void *process)
+class EcaFd : public EcaIndices1
 {
-  ModuleEcaEtr eca;
-  eca.init(process);
-  eca.run();
-  eca.close();
-  return nullptr;
-}
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaFd",
+    .operators = { { "eca_fd", 0, CMP_DATE, EcaFdHelp }, { "etccdi_fd", 0, CMP_YEAR, EcaFdHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaFd> registration = RegisterEntry<EcaFd>(module);
+  int ECA_FD, ETCCDI_FD;
 
-class ModuleEcaFd : public EcaIndices1
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    int ECA_FD, ETCCDI_FD;
-    ECA_FD = cdo_operator_add("eca_fd", 0, CMP_DATE, nullptr);
-    ETCCDI_FD = cdo_operator_add("etccdi_fd", 0, CMP_YEAR, nullptr);
+    ECA_FD = module.get_id("eca_fd");
+    ETCCDI_FD = module.get_id("etccdi_fd");
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -692,16 +721,6 @@ public:
   }
 };
 
-void *
-EcaFd(void *process)
-{
-  ModuleEcaFd eca;
-  eca.init(process);
-  eca.run();
-  eca.close();
-  return nullptr;
-}
-
 /*
  * Definition of GSL: (Thermal) Growing Season Length start at the first span
  * of at least 6 (argN) days with T > 5.0°C (argT) in first half of the year
@@ -711,8 +730,19 @@ EcaFd(void *process)
  * december, whereas for the southern hemisphere is goes from july to june!
  * Hence, at least 18 Month of data is needed for computing the gsl of the whole earth.
  */
-class ModuleEcaGsl : public EcaIndices4
+class EcaGsl : public EcaIndices4
 {
+public:
+  using EcaIndices4::EcaIndices4;
+  inline static CdoModule module = {
+    .name = "EcaGsl",
+    .operators = { { "eca_gsl", 0, CMP_YEAR, EcaGslHelp }, { "etccdi_gsl", EcaGslHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaGsl> registration = RegisterEntry<EcaGsl>(module);
   int argN = 6;
   double argT = 5.0;
   double minLandFraction = 0.5;
@@ -720,17 +750,15 @@ class ModuleEcaGsl : public EcaIndices4
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("eca_gsl", 0, CMP_YEAR, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0) argN = parameter_to_int(cdo_operator_argv(0));
     if (cdo_operator_argc() > 1) argT = parameter_to_double(cdo_operator_argv(1));
     if (cdo_operator_argc() > 2) minLandFraction = parameter_to_double(cdo_operator_argv(2));
 
-    sprintf(longname, GSL_LONGNAME, argN, argT, argN, argT);
+    std::snprintf(longname, sizeof(longname), GSL_LONGNAME, argN, argT, argN, argT);
 
     request.name = GSL_NAME;
     request.longname = longname;
@@ -748,29 +776,27 @@ public:
   }
 };
 
-void *
-EcaGsl(void *process)
-{
-  ModuleEcaGsl ecaGsl;
-  ecaGsl.init(process);
-  ecaGsl.run();
-  ecaGsl.close();
-
-  return nullptr;
-}
-
-class ModuleEcaHd : public EcaIndices1
+class EcaHd : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaHd",
+    .operators = { { "eca_hd", 0, CMP_DATE, EcaHdHelp }, { "etccdi_hd", EcaHdHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaHd> registration = RegisterEntry<EcaHd>(module);
   double argX = 17.0;
   double argA = 17.0;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_hd", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -792,28 +818,27 @@ public:
   }
 };
 
-void *
-EcaHd(void *process)
-{
-  ModuleEcaHd ecaHd;
-  ecaHd.init(process);
-  ecaHd.run();
-  ecaHd.close();
-  return nullptr;
-}
-
-class ModuleEcaHwdi : public EcaIndices2
+class EcaHwdi : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaHwdi",
+    .operators = { { "eca_hwdi", 0, CMP_DATE, EcaHwdiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaHwdi> registration = RegisterEntry<EcaHwdi>(module);
   int argN = 6;
   double argT = 5.0;
   char longname[sizeof(HWDI_LONGNAME) + 80];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("eca_hwdi", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 2)
@@ -830,7 +855,7 @@ public:
         if (cdo_operator_argc() > 1) argT = parameter_to_double(cdo_operator_argv(1));
       }
 
-    sprintf(longname, HWDI_LONGNAME, argN, argT);
+    std::snprintf(longname, sizeof(longname), HWDI_LONGNAME, argN, argT);
 
     request.var1.name = HWDI_NAME;
     request.var1.longname = longname;
@@ -851,29 +876,29 @@ public:
   }
 };
 
-void *
-EcaHwdi(void *process)
-{
-  ModuleEcaHwdi ecaHwdi;
-  ecaHwdi.init(process);
-  ecaHwdi.run();
-  ecaHwdi.close();
-  return nullptr;
-}
-
-class ModuleEcaHwfi : public EcaIndices2
+class EcaHwfi : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaHwfi",
+    .operators = { { "eca_hwfi", 0, CMP_DATE, EcaHwfiHelp }, { "etccdi_wsdi", 0, CMP_YEAR, EcaHwfiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaHwfi> registration = RegisterEntry<EcaHwfi>(module);
+  int ECA_HWFI, ETCCDI_WSDI;
   int argN = 6;
   char longname[sizeof(HWFI_LONGNAME) + 40];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    int ECA_HWFI, ETCCDI_WSDI;
-    ECA_HWFI = cdo_operator_add("eca_hwfi", 0, CMP_DATE, nullptr);
-    ETCCDI_WSDI = cdo_operator_add("etccdi_wsdi", 0, CMP_YEAR, nullptr);
+    ECA_HWFI = module.get_id("eca_hwfi");
+    ETCCDI_WSDI = module.get_id("etccdi_wsdi");
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 1)
@@ -890,7 +915,7 @@ public:
 
     if (ECA_HWFI == cdo_operator_id())
       {
-        sprintf(longname, HWFI_LONGNAME, argN);
+        std::snprintf(longname, sizeof(longname), HWFI_LONGNAME, argN);
         request.var1.name = HWFI_NAME;
         request.var1.longname = longname;
         request.var1.units = HWFI_UNITS;
@@ -917,27 +942,28 @@ public:
   }
 };
 
-void *
-EcaHwfi(void *process)
+class EcaId : public EcaIndices1
 {
-  ModuleEcaHwfi ecaHwfi;
-  ecaHwfi.init(process);
-  ecaHwfi.run();
-  ecaHwfi.close();
-  return nullptr;
-}
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaId",
+    .operators = { { "eca_id", 0, CMP_DATE, EcaIdHelp }, { "etccdi_id", 0, CMP_YEAR, EcaIdHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaId> registration = RegisterEntry<EcaId>(module);
+  int ECA_ID, ETCCDI_ID;
 
-class ModuleEcaId : public EcaIndices1
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    int ECA_ID, ETCCDI_ID;
-    ECA_ID = cdo_operator_add("eca_id", 0, CMP_DATE, nullptr);
-    ETCCDI_ID = cdo_operator_add("etccdi_id", 0, CMP_YEAR, nullptr);
+    ECA_ID = module.get_id("eca_id");
+    ETCCDI_ID = module.get_id("etccdi_id");
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -966,30 +992,30 @@ public:
   }
 };
 
-void *
-EcaId(void *process)
-{
-  ModuleEcaId ecaId;
-  ecaId.init(process);
-  ecaId.run();
-  ecaId.close();
-  return nullptr;
-}
-
-class ModuleEcaSu : public EcaIndices1
+class EcaSu : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaSu",
+    .operators = { { "eca_su", 0, CMP_DATE, EcaSuHelp }, { "etccdi_su", 0, CMP_YEAR, EcaSuHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaSu> registration = RegisterEntry<EcaSu>(module);
+  int ECA_SU, ETCCDI_SU;
   double argT = 25.0;
   char longname[sizeof(SU_LONGNAME) + 40];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    int ECA_SU = 0, ETCCDI_SU = 0;
-    ECA_SU = cdo_operator_add("eca_su", 0, CMP_DATE, nullptr);
-    ETCCDI_SU = cdo_operator_add("etccdi_su", 0, CMP_YEAR, nullptr);
+    ECA_SU = module.get_id("eca_su");
+    ETCCDI_SU = module.get_id("etccdi_su");
     set_default_compare_type(request.compare_type);
     if (cdo_operator_argc() > 0) argT = parameter_to_double(cdo_operator_argv(0));
     if (cdo_operator_argc() > 1)
@@ -1001,7 +1027,7 @@ public:
 
     if (ECA_SU == cdo_operator_id())
       {
-        sprintf(longname, SU_LONGNAME, argT);
+        std::snprintf(longname, sizeof(longname), SU_LONGNAME, argT);
         request.var1.name = SU_NAME;
         request.var1.longname = longname;
         request.var1.refdate = ECA_refdate;
@@ -1020,25 +1046,25 @@ public:
   }
 };
 
-void *
-EcaSu(void *process)
+class EcaTg10p : public EcaIndices2
 {
-  ModuleEcaSu ecaSu;
-  ecaSu.init(process);
-  ecaSu.run();
-  ecaSu.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaTg10p",
+    .operators = { { "eca_tg10p", 0, CMP_DATE, EcaTg10pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTg10p> registration = RegisterEntry<EcaTg10p>(module);
 
-class ModuleEcaTg10p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_tg10p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = TG10P_NAME;
@@ -1051,25 +1077,25 @@ public:
   }
 };
 
-void *
-EcaTg10p(void *process)
+class EcaTg90p : public EcaIndices2
 {
-  ModuleEcaTg10p ecatg10p;
-  ecatg10p.init(process);
-  ecatg10p.run();
-  ecatg10p.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaTg90p",
+    .operators = { { "eca_tg90p", 0, CMP_DATE, EcaTg90pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTg90p> registration = RegisterEntry<EcaTg90p>(module);
 
-class ModuleEcaTg90p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_tg90p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = TG90P_NAME;
@@ -1082,25 +1108,25 @@ public:
   }
 };
 
-void *
-EcaTg90p(void *process)
+class EcaTn10p : public EcaIndices2
 {
-  ModuleEcaTg90p ecatg90p;
-  ecatg90p.init(process);
-  ecatg90p.run();
-  ecatg90p.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaTn10p",
+    .operators = { { "eca_tn10p", 0, CMP_DATE, EcaTn10pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTn10p> registration = RegisterEntry<EcaTn10p>(module);
 
-class ModuleEcaTn10p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_tn10p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = TN10P_NAME;
@@ -1112,25 +1138,26 @@ public:
     request.var1.epilog = PERCENT_OF_TIME;
   }
 };
-void *
-EcaTn10p(void *process)
-{
-  ModuleEcaTn10p ecatn10p;
-  ecatn10p.init(process);
-  ecatn10p.run();
-  ecatn10p.close();
-  return nullptr;
-}
 
-class ModuleEcaTn90p : public EcaIndices2
+class EcaTn90p : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaTn90p",
+    .operators = { { "eca_tn90p", 0, CMP_DATE, EcaTn90pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTn90p> registration = RegisterEntry<EcaTn90p>(module);
+
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_tn90p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = TN90P_NAME;
@@ -1143,31 +1170,31 @@ public:
   }
 };
 
-void *
-EcaTn90p(void *process)
-{
-  ModuleEcaTn90p ecatn90p;
-  ecatn90p.init(process);
-  ecatn90p.run();
-  ecatn90p.close();
-  return nullptr;
-}
-
-class ModuleEcaTr : public EcaIndices1
+class EcaTr : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaTr",
+    .operators = { { "eca_tr", 0, CMP_DATE, EcaTrHelp }, { "etccdi_tr", 0, CMP_YEAR, EcaTrHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTr> registration = RegisterEntry<EcaTr>(module);
+  int ECA_TR, ETCCDI_TR;
   double argT = 20.0;
   char longname[1024];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
     set_default_compare_type(request.compare_type);
 
-    int ECA_TR = 0, ETCCDI_TR = 0;
-    ECA_TR = cdo_operator_add("eca_tr", 0, CMP_DATE, nullptr);
-    ETCCDI_TR = cdo_operator_add("etccdi_tr", 0, CMP_YEAR, nullptr);
+    ECA_TR = module.get_id("eca_tr");
+    ETCCDI_TR = module.get_id("etccdi_tr");
     if (cdo_operator_argc() > 0) argT = parameter_to_double(cdo_operator_argv(0));
     if (cdo_operator_argc() > 1)
       {
@@ -1178,7 +1205,7 @@ public:
 
     if (ECA_TR == cdo_operator_id())
       {
-        sprintf(longname, TR_LONGNAME, argT);
+        std::snprintf(longname, sizeof(longname), TR_LONGNAME, argT);
         request.var1.name = TR_NAME;
         request.var1.longname = longname;
         request.var1.units = TR_UNITS;
@@ -1198,25 +1225,25 @@ public:
   }
 };
 
-void *
-EcaTr(void *process)
+class EcaTx10p : public EcaIndices2
 {
-  ModuleEcaTr ecaTr;
-  ecaTr.init(process);
-  ecaTr.run();
-  ecaTr.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaTx10p",
+    .operators = { { "eca_tx10p", 0, CMP_DATE, EcaTx10pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTx10p> registration = RegisterEntry<EcaTx10p>(module);
 
-class ModuleEcaTx10p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_tx10p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = TX10P_NAME;
@@ -1229,32 +1256,30 @@ public:
   }
 };
 
-void *
-EcaTx10p(void *process)
+class EcaTx90p : public EcaIndices2
 {
-  ModuleEcaTx10p ecaTx10p;
-
-  ecaTx10p.init(process);
-  ecaTx10p.run();
-  ecaTx10p.close();
-
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaTx90p",
+    .operators = { { "eca_tx90p", 0, CMP_DATE, EcaTx90pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaTx90p> registration = RegisterEntry<EcaTx90p>(module);
 
-class ModuleEcaTx90p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("eca_tx90p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
       {
         if ('m' == cdo_operator_argv(0)[0])
-          cdo_operator_add("eca_tx90p", 0, CMP_MONTH, nullptr);  // monthly mode
+          request.compare_type = CMP_MONTH;
         else
           cdo_warning("Parameter value '%s' is invalid. The only valid value is "
                       "'m' indicating monthly mode. Operating in yearly mode now.",
@@ -1271,20 +1296,22 @@ public:
   }
 };
 
-void *
-EcaTx90p(void *process)
-{
-  ModuleEcaTx90p ecaTx90p;
-  ecaTx90p.init(process);
-  ecaTx90p.run();
-  ecaTx90p.close();
-  return nullptr;
-}
-
 // ECA precipitation indices
 
-class ModuleEcaCdd : public EcaIndices1
+class EcaCdd : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaCdd",
+    .operators = { { "eca_cdd", 0, CMP_DATE, EcaCddHelp }, { "etccdi_cdd", 0, CMP_YEAR, EcaCddHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaCdd> registration = RegisterEntry<EcaCdd>(module);
+  int ECA_CDD, ETCCDI_CDD;
   double threshold = 1;
   int ndays = 5;
   char cdd_longname[1024];
@@ -1293,13 +1320,11 @@ class ModuleEcaCdd : public EcaIndices1
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    int ECA_CDD = 0, ETCCDI_CDD = 0;
-    ECA_CDD = cdo_operator_add("eca_cdd", 0, CMP_DATE, nullptr);
-    ETCCDI_CDD = cdo_operator_add("etccdi_cdd", 0, CMP_YEAR, nullptr);
+    ECA_CDD = module.get_id("eca_cdd");
+    ETCCDI_CDD = module.get_id("etccdi_cdd");
 
     set_default_compare_type(request.compare_type);
     if (cdo_operator_argc() > 3)
@@ -1316,9 +1341,9 @@ public:
         if (cdo_operator_argc() == 2) ndays = parameter_to_int(cdo_operator_argv(1));
       }
 
-    sprintf(cdd_longname, CDD_LONGNAME, threshold);
-    sprintf(cdd_longname2, CDD_LONGNAME2, ndays);
-    sprintf(cdd_name2, CDD_NAME2, ndays);
+    std::snprintf(cdd_longname, sizeof(cdd_longname), CDD_LONGNAME, threshold);
+    std::snprintf(cdd_longname2, sizeof(cdd_longname2), CDD_LONGNAME2, ndays);
+    std::snprintf(cdd_name2, sizeof(cdd_name2), CDD_NAME2, ndays);
 
     if (ECA_CDD == cdo_operator_id())
       {
@@ -1348,18 +1373,21 @@ public:
   }
 };
 
-void *
-EcaCdd(void *process)
-{
-  ModuleEcaCdd ecaCdd;
-  ecaCdd.init(process);
-  ecaCdd.run();
-  ecaCdd.close();
-  return nullptr;
-}
-
-class ModuleEcaCwd : public EcaIndices1
+class EcaCwd : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaCwd",
+    .operators = { { "eca_cwd", 0, CMP_DATE, EcaCwdHelp }, { "etccdi_cwd", 0, CMP_YEAR, EcaCwdHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaCwd> registration = RegisterEntry<EcaCwd>(module);
+
+  int ECA_CWD, ETCCDI_CWD;
   double threshold = 1;
   int ndays = 5;
   char cwd_longname[1024];
@@ -1368,13 +1396,11 @@ class ModuleEcaCwd : public EcaIndices1
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
+    ECA_CWD = module.get_id("eca_cwd");
+    ETCCDI_CWD = module.get_id("etccdi_cwd");
 
-    int ECA_CWD = 0, ETCCDI_CWD = 0;
-    ECA_CWD = cdo_operator_add("eca_cwd", 0, CMP_DATE, nullptr);
-    ETCCDI_CWD = cdo_operator_add("etccdi_cwd", 0, CMP_YEAR, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 3)
@@ -1391,9 +1417,9 @@ public:
         if (cdo_operator_argc() == 2) ndays = parameter_to_int(cdo_operator_argv(1));
       }
 
-    sprintf(cwd_longname, CWD_LONGNAME, threshold);
-    sprintf(cwd_longname2, CWD_LONGNAME2, ndays);
-    sprintf(cwd_name2, CWD_NAME2, ndays);
+    std::snprintf(cwd_longname, sizeof(cwd_longname), CWD_LONGNAME, threshold);
+    std::snprintf(cwd_longname2, sizeof(cwd_longname2), CWD_LONGNAME2, ndays);
+    std::snprintf(cwd_name2, sizeof(cwd_name2), CWD_NAME2, ndays);
 
     if (ECA_CWD == cdo_operator_id())
       {
@@ -1422,34 +1448,39 @@ public:
   }
 };
 
-void *
-EcaCwd(void *process)
-{
-  ModuleEcaCwd ecaCwd;
-  ecaCwd.init(process);
-  ecaCwd.run();
-  ecaCwd.close();
-  return nullptr;
-}
-
-class ModuleEcaPd : public EcaIndices1
+class EcaPd : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaPd",
+    .operators = { { "eca_pd", 0, CMP_DATE, EcaPdHelp },
+                   { "eca_r10mm", 0, CMP_DATE, EcaPdHelp },
+                   { "eca_r20mm", 0, CMP_DATE, EcaPdHelp },
+                   { "etccdi_r1mm", 0, CMP_DATE, EcaPdHelp },
+                   { "etccdi_r10mm", 0, CMP_DATE, EcaPdHelp },
+                   { "etccdi_r20mm", 0, CMP_DATE, EcaPdHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaPd> registration = RegisterEntry<EcaPd>(module);
+  int ECA_PD, ETCCDI_R1MM, ECA_R10MM, ETCCDI_R10MM, ECA_R20MM, ETCCDI_R20MM;
   char lnamebuffer[1024];
   double threshold = 0;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    // clang-format off
-    auto ECA_PD       = cdo_operator_add("eca_pd",       0, CMP_DATE, nullptr);
-    auto ETCCDI_R1MM    = cdo_operator_add("etccdi_r1mm",  0, CMP_DATE, nullptr);
-    auto ECA_R10MM    = cdo_operator_add("eca_r10mm",    0, CMP_DATE, nullptr);
-    auto ETCCDI_R10MM = cdo_operator_add("etccdi_r10mm", 0, CMP_DATE, nullptr);
-    auto ECA_R20MM    = cdo_operator_add("eca_r20mm",    0, CMP_DATE, nullptr);
-    auto ETCCDI_R20MM = cdo_operator_add("etccdi_r20mm", 0, CMP_DATE, nullptr);
-    // clang-format on
+    ECA_PD = module.get_id("eca_pd");
+    ETCCDI_R1MM = module.get_id("etccdi_r1mm");
+    ECA_R10MM = module.get_id("eca_r10mm");
+    ETCCDI_R10MM = module.get_id("etccdi_r10mm");
+    ECA_R20MM = module.get_id("eca_r20mm");
+    ETCCDI_R20MM = module.get_id("etccdi_r20mm");
+
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -1459,7 +1490,7 @@ public:
         if (strstr(cdo_operator_argv(0).c_str(), "=") || cdo_operator_argc() > 1)
           {
             if (cdo_operator_argc() > 1) params = std::vector<std::string>(params.begin() + 1, params.end());
-            if (kvlist.parse_arguments(1, params) != 0) cdo_abort("Argument parse error!");
+            if (kvlist.parse_arguments(params) != 0) cdo_abort("Argument parse error!");
             auto kv = kvlist.search("freq");
             if (kv && kv->nvalues > 0)
               {
@@ -1482,7 +1513,7 @@ public:
             if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
             if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
             threshold = parameter_to_double(cdo_operator_argv(0));
-            sprintf(lnamebuffer, PD_LONGNAME, threshold);
+            std::snprintf(lnamebuffer, sizeof(lnamebuffer), PD_LONGNAME, threshold);
             request.var1.name = PD_NAME;
             request.var1.longname = lnamebuffer;
             request.var1.units = PD_UNITS;
@@ -1541,25 +1572,25 @@ public:
   }
 };
 
-void *
-EcaPd(void *process)
+class EcaR75p : public EcaIndices2
 {
-  ModuleEcaPd ecaPd;
-  ecaPd.init(process);
-  ecaPd.run();
-  ecaPd.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR75p",
+    .operators = { { "eca_r75p", 0, CMP_DATE, EcaR75pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR75p> registration = RegisterEntry<EcaR75p>(module);
 
-class ModuleEcaR75p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r75p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R75P_NAME;
@@ -1573,25 +1604,25 @@ public:
   }
 };
 
-void *
-EcaR75p(void *process)
+class EcaR75ptot : public EcaIndices2
 {
-  ModuleEcaR75p ecaR75p;
-  ecaR75p.init(process);
-  ecaR75p.run();
-  ecaR75p.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR75ptot",
+    .operators = { { "eca_r75ptot", 0, CMP_DATE, EcaR75ptotHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR75ptot> registration = RegisterEntry<EcaR75ptot>(module);
 
-class ModuleEcaR75ptot : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r75ptot", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R75PTOT_NAME;
@@ -1605,25 +1636,25 @@ public:
   }
 };
 
-void *
-EcaR75ptot(void *process)
+class EcaR90p : public EcaIndices2
 {
-  ModuleEcaR75ptot ecaR75ptot;
-  ecaR75ptot.init(process);
-  ecaR75ptot.run();
-  ecaR75ptot.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR90p",
+    .operators = { { "eca_r90p", 0, CMP_DATE, EcaR90pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR90p> registration = RegisterEntry<EcaR90p>(module);
 
-class ModuleEcaR90p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r90p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R90P_NAME;
@@ -1637,24 +1668,25 @@ public:
   }
 };
 
-void *
-EcaR90p(void *process)
-{
-  ModuleEcaR90p ecaR90p;
-  ecaR90p.init(process);
-  ecaR90p.run();
-  ecaR90p.close();
-  return nullptr;
-}
-class ModuleEcaR95p : public EcaIndices2
+class EcaR95p : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR95p",
+    .operators = { { "eca_r95p", 0, CMP_DATE, EcaR95pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR95p> registration = RegisterEntry<EcaR95p>(module);
+
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r95p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R95P_NAME;
@@ -1668,25 +1700,25 @@ public:
   }
 };
 
-void *
-EcaR95p(void *process)
+class EcaR90ptot : public EcaIndices2
 {
-  ModuleEcaR95p ecaR95p;
-  ecaR95p.init(process);
-  ecaR95p.run();
-  ecaR95p.close();
-  return nullptr;
-};
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR90ptot",
+    .operators = { { "eca_r90ptot", 0, CMP_DATE, EcaR90ptotHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR90ptot> registration = RegisterEntry<EcaR90ptot>(module);
 
-class ModuleEcaR90ptot : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r90ptot", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R90PTOT_NAME;
@@ -1700,57 +1732,56 @@ public:
   }
 };
 
-void *
-EcaR90ptot(void *process)
-{
-  ModuleEcaR90ptot ecaR90ptot;
-  ecaR90ptot.init(process);
-  ecaR90ptot.run();
-  ecaR90ptot.close();
-  return nullptr;
-}
-
-//class ModuleEcaR96p : public EcaIndices2
+// class ModuleEcaR96p : public EcaIndices2
 //{
-//public:
-//  void
-//  init(void *process)
-//  {
-//    cdo_initialize(process);
-//
-//    cdo_operator_add("eca_r95p", 0, CMP_DATE, nullptr);
-//    set_default_compare_type(request.compare_type);
+// public:
+//   void
+//   init()
+//   {
+// //
+//     cdo_operator_add("eca_r95p", 0, CMP_DATE, nullptr);
+//     set_default_compare_type(request.compare_type);
 //
-//    request.var1.name = R95P_NAME;
-//    request.var1.longname = R95P_LONGNAME;
-//    request.var1.refdate = ECA_refdate;
-//    request.var1.units = R95P_UNITS;
-//    request.var1.f1 = vfarselgec;
-//    request.var1.f3 = vfarselgt;
-//    request.var1.f4 = vfarnum;
-//    request.var1.epilog = PERCENT_OF_TIME;
-//  }
-//};
+//     request.var1.name = R95P_NAME;
+//     request.var1.longname = R95P_LONGNAME;
+//     request.var1.refdate = ECA_refdate;
+//     request.var1.units = R95P_UNITS;
+//     request.var1.f1 = vfarselgec;
+//     request.var1.f3 = vfarselgt;
+//     request.var1.f4 = vfarnum;
+//     request.var1.epilog = PERCENT_OF_TIME;
+//   }
+// };
 //
-//void *
-//EcaR96p(void *process)
+// void *
+// EcaR96p(void *process)
 //{
-//  ModuleEcaR96p ecaR96p;
-//  ecaR96p.init(process);
-//  ecaR96p.run();
-//  ecaR96p.close();
-//  return nullptr;
-//}
-
-class ModuleEcaR95ptot : public EcaIndices2
+//   ModuleEcaR96p ecaR96p;
+//   ecaR96p.init(process);
+//   ecaR96p.run();
+//   ecaR96p.close();
+//   return nullptr;
+// }
+
+class EcaR95ptot : public EcaIndices2
 {
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR95ptot",
+    .operators = { { "eca_r95ptot", 0, CMP_DATE, EcaR95ptotHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR95ptot> registration = RegisterEntry<EcaR95ptot>(module);
+
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r95ptot", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R95PTOT_NAME;
@@ -1764,25 +1795,25 @@ public:
   }
 };
 
-void *
-EcaR95ptot(void *process)
+class EcaR99p : public EcaIndices2
 {
-  ModuleEcaR95ptot ecaR95ptot;
-  ecaR95ptot.init(process);
-  ecaR95ptot.run();
-  ecaR95ptot.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR99p",
+    .operators = { { "eca_r99p", 0, CMP_DATE, EcaR99pHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR99p> registration = RegisterEntry<EcaR99p>(module);
 
-class ModuleEcaR99p : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r99p", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R99P_NAME;
@@ -1796,25 +1827,25 @@ public:
   }
 };
 
-void *
-EcaR99p(void *process)
+class EcaR99ptot : public EcaIndices2
 {
-  ModuleEcaR99p ecaR99p;
-  ecaR99p.init(process);
-  ecaR99p.run();
-  ecaR99p.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "EcaR99ptot",
+    .operators = { { "eca_r99ptot", 0, CMP_DATE, EcaR99ptotHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaR99ptot> registration = RegisterEntry<EcaR99ptot>(module);
 
-class ModuleEcaR99ptot : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    cdo_operator_add("eca_r99ptot", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = R99PTOT_NAME;
@@ -1827,28 +1858,27 @@ public:
   }
 };
 
-void *
-EcaR99ptot(void *process)
-{
-  ModuleEcaR99ptot ecaR99ptot;
-  ecaR99ptot.init(process);
-  ecaR99ptot.run();
-  ecaR99ptot.close();
-  return nullptr;
-}
-
-class ModuleEcaRr1 : public EcaIndices1
+class EcaRr1 : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaRr1",
+    .operators = { { "eca_rr1", 0, CMP_DATE, EcaRr1Help } },
+    .aliases = { { "eca_r1mm", "eca_rr1" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaRr1> registration = RegisterEntry<EcaRr1>(module);
   char longname[1024];
 
 public:
   void
-  init(void *process)
+  init()
   {
     double threshold = 1;
 
-    cdo_initialize(process);
-    cdo_operator_add("eca_rr1", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 2)
@@ -1864,7 +1894,7 @@ public:
         if (cdo_operator_argc() == 1) threshold = parameter_to_double(cdo_operator_argv(0));
       }
 
-    sprintf(longname, RR1_LONGNAME, threshold);
+    std::snprintf(longname, sizeof(longname), RR1_LONGNAME, threshold);
 
     request.var1.name = RR1_NAME;
     request.var1.longname = longname;
@@ -1875,27 +1905,30 @@ public:
   }
 };
 
-void *
-EcaRr1(void *process)
+class EcaRx1day : public EcaIndices1
 {
-  ModuleEcaRr1 ecaRr1;
-  ecaRr1.init(process);
-  ecaRr1.run();
-  ecaRr1.close();
-  return nullptr;
-}
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaRx1day",
+    .operators = { { "eca_rx1day", 0, CMP_DATE, EcaRx1dayHelp },
+                   { "etccdi_rx1day", 0, CMP_YEAR, EcaRx1dayHelp },
+                   { "etccdi_rx1daymon", EcaRx1dayHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaRx1day> registration = RegisterEntry<EcaRx1day>(module);
+  int ECA_RX1DAY, ETCCDI_RX1DAY;
 
-class ModuleEcaRx1day : public EcaIndices1
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    int ECA_RX1DAY, ETCCDI_RX1DAY;
-    ECA_RX1DAY = cdo_operator_add("eca_rx1day", 0, CMP_DATE, nullptr);
-    ETCCDI_RX1DAY = cdo_operator_add("etccdi_rx1day", 0, CMP_YEAR, nullptr);
+    ECA_RX1DAY = module.get_id("eca_rx1day");
+    ETCCDI_RX1DAY = module.get_id("etccdi_rx1day");
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -1922,31 +1955,32 @@ public:
   }
 };
 
-void *
-EcaRx1day(void *process)
-{
-  ModuleEcaRx1day ecaRx1day;
-  ecaRx1day.init(process);
-  ecaRx1day.run();
-  ecaRx1day.close();
-  return nullptr;
-}
-
-class ModuleEcaRx5day : public EcaIndices1
+class EcaRx5day : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaRx5day",
+    .operators = { { "eca_rx5day", 0, CMP_DATE, EcaRx5dayHelp },
+                   { "etccdi_rx5day", 0, CMP_YEAR, EcaRx5dayHelp },
+                   { "etccdi_rx5daymon", EcaRx5dayHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaRx5day> registration = RegisterEntry<EcaRx5day>(module);
+  int ECA_RX5DAY, ETCCDI_RX5DAY;
   char longname2[sizeof(RX5DAY_LONGNAME2) + 40];
 
 public:
   void
-  init(void *process)
+  init()
   {
     double argX = 50.0;
 
-    cdo_initialize(process);
-
-    int ECA_RX5DAY, ETCCDI_RX5DAY;
-    ECA_RX5DAY = cdo_operator_add("eca_rx5day", 0, CMP_DATE, nullptr);
-    ETCCDI_RX5DAY = cdo_operator_add("etccdi_rx5day", 0, CMP_YEAR, nullptr);
+    ECA_RX5DAY = module.get_id("eca_rx5day");
+    ETCCDI_RX5DAY = module.get_id("etccdi_rx5day");
     set_default_compare_type(request.compare_type);
     if (cdo_operator_argc() > 1)
       {
@@ -1956,7 +1990,7 @@ public:
         set_compare_type_from_params(request.compare_type, params);
       }
 
-    sprintf(longname2, RX5DAY_LONGNAME2, argX);
+    std::snprintf(longname2, sizeof(longname2), RX5DAY_LONGNAME2, argX);
 
     if (ECA_RX5DAY == cdo_operator_id())
       {
@@ -1982,29 +2016,29 @@ public:
   }
 };
 
-void *
-EcaRx5day(void *process)
-{
-  ModuleEcaRx5day ecaRx5day;
-  ecaRx5day.init(process);
-  ecaRx5day.run();
-  ecaRx5day.close();
-  return nullptr;
-}
-
-class ModuleEcaSdii : public EcaIndices1
+class EcaSdii : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "EcaSdii",
+    .operators = { { "eca_sdii", 0, CMP_DATE, EcaSdiiHelp }, { "etccdi_sdii", 0, CMP_DATE, EcaSdiiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EcaSdii> registration = RegisterEntry<EcaSdii>(module);
+  int ECA_SDII, ETCCDI_SDII;
   char longname[1024];
   double threshold = 1;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    int ECA_SDII, ETCCDI_SDII;
-    ECA_SDII = cdo_operator_add("eca_sdii", 0, CMP_DATE, nullptr);
-    ETCCDI_SDII = cdo_operator_add("etccdi_sdii", 0, CMP_DATE, nullptr);
+    ECA_SDII = module.get_id("eca_sdii");
+    ETCCDI_SDII = module.get_id("etccdi_sdii");
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
@@ -2023,7 +2057,7 @@ public:
 
     if (ECA_SDII == cdo_operator_id())
       {
-        sprintf(longname, SDII_LONGNAME, threshold);
+        std::snprintf(longname, sizeof(longname), SDII_LONGNAME, threshold);
         request.var1.name = SDII_NAME;
         request.var1.longname = longname;
         request.var1.units = SDII_UNITS;
@@ -2042,24 +2076,24 @@ public:
   }
 };
 
-void *
-EcaSdii(void *process)
+class Fdns : public EcaIndices2
 {
-  ModuleEcaSdii sdii;
-  sdii.init(process);
-  sdii.run();
-  sdii.close();
-  return nullptr;
-}
+public:
+  using EcaIndices2::EcaIndices2;
+  inline static CdoModule module = {
+    .name = "Fdns",
+    .operators = { { "fdns", 0, CMP_DATE, FdnsHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Fdns> registration = RegisterEntry<Fdns>(module);
 
-class ModuleFdns : public EcaIndices2
-{
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("fdns", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     request.var1.name = FDNS_NAME;
@@ -2075,27 +2109,26 @@ public:
   }
 };
 
-void *
-Fdns(void *process)
-{
-  ModuleFdns fdns;
-  fdns.init(process);
-  fdns.run();
-  fdns.close();
-  return nullptr;
-}
-
-class ModuleStrwin : public EcaIndices1
+class Strwin : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "Strwin",
+    .operators = { { "strwin", 0, CMP_DATE, StrwinHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Strwin> registration = RegisterEntry<Strwin>(module);
   double maxWind = 10.5;
   char longname[sizeof(STRWIN_LONGNAME) + 40];
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("strwin", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 2)
@@ -2111,7 +2144,7 @@ public:
         if (cdo_operator_argc() > 0) maxWind = parameter_to_double(cdo_operator_argv(0));
       }
 
-    sprintf(longname, STRWIN_LONGNAME, maxWind);
+    std::snprintf(longname, sizeof(longname), STRWIN_LONGNAME, maxWind);
 
     request.var1.name = STRWIN_NAME;
     request.var1.longname = longname;
@@ -2130,26 +2163,25 @@ public:
   }
 };
 
-void *
-Strwin(void *process)
-{
-  ModuleStrwin strwin;
-  strwin.init(process);
-  strwin.run();
-  strwin.close();
-  return nullptr;
-}
-
-class ModuleStrbre : public EcaIndices1
+class Strbre : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "Strbre",
+    .operators = { { "strbre", 0, CMP_DATE, StrbreHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Strbre> registration = RegisterEntry<Strbre>(module);
   double maxWind = 10.5;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("strbre", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -2175,26 +2207,25 @@ public:
   }
 };
 
-void *
-Strbre(void *process)
-{
-  ModuleStrbre strbre;
-  strbre.init(process);
-  strbre.run();
-  strbre.close();
-  return nullptr;
-}
-
-class ModuleStrgal : public EcaIndices1
+class Strgal : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "Strgal",
+    .operators = { { "strgal", 0, CMP_DATE, StrgalHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Strgal> registration = RegisterEntry<Strgal>(module);
   double maxWind = 20.5;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("strgal", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -2219,26 +2250,26 @@ public:
     request.var2.h3 = field2_max;
   }
 };
-void *
-Strgal(void *process)
-{
-  ModuleStrgal strgal;
-  strgal.init(process);
-  strgal.run();
-  strgal.close();
-  return nullptr;
-}
 
-class ModuleHurr : public EcaIndices1
+class Hurr : public EcaIndices1
 {
+public:
+  using EcaIndices1::EcaIndices1;
+  inline static CdoModule module = {
+    .name = "Hurr",
+    .operators = { { "hurr", 0, CMP_DATE, HurrHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Hurr> registration = RegisterEntry<Hurr>(module);
   double maxWind = 32.5;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("hurr", 0, CMP_DATE, nullptr);
     set_default_compare_type(request.compare_type);
 
     if (cdo_operator_argc() > 0)
@@ -2263,13 +2294,3 @@ public:
     request.var2.h3 = field2_max;
   }
 };
-
-void *
-Hurr(void *process)
-{
-  ModuleHurr hurr;
-  hurr.init(process);
-  hurr.run();
-  hurr.close();
-  return nullptr;
-}
diff --git a/src/Echam5ini.cc b/src/Echam5ini.cc
index 12c5cebd7f6be48d95a29715026d9723ab98b885..acca9422effad4776cee5fcc3eb1fb3d1c380268 100644
--- a/src/Echam5ini.cc
+++ b/src/Echam5ini.cc
@@ -609,13 +609,14 @@ export_e5ml(const char *filename, const std::vector<VAR> &vars, int nvars, int v
 #endif
 }
 
-class ModuleEcham5ini
+class Echam5ini : public Process
 {
+  using Process::Process;
+
+public:
   void
   ex_e5ml()
   {
-    std::string name, longname, units;
-
     auto streamID1 = cdo_open_read(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
@@ -631,9 +632,9 @@ class ModuleEcham5ini
     for (int varID = 0; varID < nvars; ++varID)
       {
         auto code = varList1[varID].code;
-        name = varList1[varID].name;
-        longname = varList1[varID].longname;
-        units = varList1[varID].units;
+        auto &name = varList1[varID].name;
+        auto &longname = varList1[varID].longname;
+        auto &units = varList1[varID].units;
 
         if (code < 0) code = 0;
         if (name.substr(0, 3) == "var")
@@ -714,8 +715,8 @@ class ModuleEcham5ini
         cdo_inq_record(streamID1, &varID, &levelID);
 
         auto gridsize = gridInqSize(vlistInqVarGrid(vlistID1, varID));
-        size_t nmiss;
-        cdo_read_record(streamID1, vars[varID].ptr + levelID * gridsize, &nmiss);
+        size_t numMissVals;
+        cdo_read_record(streamID1, vars[varID].ptr + levelID * gridsize, &numMissVals);
       }
 
     cdo_stream_close(streamID1);
@@ -781,49 +782,56 @@ class ModuleEcham5ini
     vlistDestroy(vlistID2);
   }
 
-  int operatorID;
-  int EXPORT_E5ML, IMPORT_E5ML;
-
-public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    IMPORT_E5ML = cdo_operator_add("import_e5ml", 0, 0, nullptr);
-    EXPORT_E5ML = cdo_operator_add("export_e5ml", 0, 0, nullptr);
-
-    operatorID = cdo_operator_id();
   }
-
   void
-  run()
+  close()
   {
-    if (operatorID == EXPORT_E5ML && process_self().m_ID != 0) cdo_abort("This operator can't be linked with other operators!");
-
-    if (operatorID == IMPORT_E5ML) { im_e5ml(); }
-    else if (operatorID == EXPORT_E5ML)
-      {
-        ex_e5ml();
-      }
+    // vlistDestroy(vlistID2);
   }
+};
+
+class Echam5ini_import : public Echam5ini
+{
+public:
+  using Echam5ini::Echam5ini;
+  inline static CdoModule module = {
+    .name = "Echam5ini",
+    .operators = { { "import_e5ml"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Echam5ini_import> registration = RegisterEntry<Echam5ini_import>(module);
 
+public:
   void
-  close()
+  run()
   {
-    // vlistDestroy(vlistID2);
-
-    cdo_finish();
+    im_e5ml();
   }
 };
-
-void *
-Echam5ini(void *process)
+class Echam5ini_export : public Echam5ini
 {
-  ModuleEcham5ini echam5ini;
-  echam5ini.init(process);
-  echam5ini.run();
-  echam5ini.close();
+public:
+  using Echam5ini::Echam5ini;
+  inline static CdoModule module = {
+    .name = "Echam5ini",
+    .operators = { { "import_e5ml"}, { "export_e5ml"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, OnlyFirst },
+  };
+  inline static RegisterEntry<Echam5ini_export> registration = RegisterEntry<Echam5ini_export>(module);
 
-  return nullptr;
-}
+public:
+  void
+  run()
+  {
+    ex_e5ml();
+  }
+};
diff --git a/src/Enlarge.cc b/src/Enlarge.cc
index e654cf0050a674eaa339c1e83be79ab31225f92e..f9fbf7836798ebebd0b8298bde80f681a00593d2 100644
--- a/src/Enlarge.cc
+++ b/src/Enlarge.cc
@@ -17,8 +17,19 @@
 #include "process_int.h"
 #include "griddes.h"
 
-class ModuleEnlarge
+class Enlarge : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Enlarge",
+    .operators = { { "enlarge", EnlargeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Enlarge> registration = RegisterEntry<Enlarge>(module);
   bool linfo = true;
 
   CdoStreamID streamID1;
@@ -39,9 +50,8 @@ class ModuleEnlarge
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(1);
 
@@ -90,8 +100,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto missval = vlistInqVarMissval(vlistID1, varID);
             auto gridID1 = vlistInqVarGrid(vlistID1, varID);
@@ -113,7 +123,7 @@ public:
                 for (size_t iy = 0; iy < ysize2; iy++)
                   for (size_t ix = 0; ix < xsize2; ix++) array2[ix + iy * xsize2] = array1[iy];
 
-                if (nmiss) nmiss *= xsize2;
+                if (numMissVals) numMissVals *= xsize2;
               }
             else if (ysize1 == 1 && xsize1 == xsize2 && xsize1 * ysize1 == gridsize1)
               {
@@ -126,18 +136,18 @@ public:
                 for (size_t iy = 0; iy < ysize2; iy++)
                   for (size_t ix = 0; ix < xsize2; ix++) array2[ix + iy * xsize2] = array1[ix];
 
-                if (nmiss) nmiss *= ysize2;
+                if (numMissVals) numMissVals *= ysize2;
               }
             else
               {
                 varray_copy(gridsize1, array1, array2);
                 for (size_t i = gridsize1; i < gridsize2; ++i) array2[i] = array1[gridsize1 - 1];
 
-                if (nmiss && DBL_IS_EQUAL(array1[gridsize1 - 1], missval)) nmiss += (gridsize2 - gridsize1);
+                if (numMissVals && DBL_IS_EQUAL(array1[gridsize1 - 1], missval)) numMissVals += (gridsize2 - gridsize1);
               }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
@@ -150,17 +160,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Enlarge(void *process)
-{
-  ModuleEnlarge enlarge;
-  enlarge.init(process);
-  enlarge.run();
-  enlarge.close();
-  return nullptr;
-}
diff --git a/src/Enlargegrid.cc b/src/Enlargegrid.cc
index 26673841c6faa450791949eca4e91b9ea8d6a3a5..070e57194c6ed0a065c65d1e3ed1d9f0892ada61 100644
--- a/src/Enlargegrid.cc
+++ b/src/Enlargegrid.cc
@@ -105,8 +105,19 @@ genGridIndex(int gridID1, int gridID2, std::vector<long> &index)
     cdo_abort("Unsupported grid type: %s", gridNamePtr(gridtype1));
 }
 
-class ModuleEnlargegrid
+class Enlargegrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Enlargegrid",
+    .operators = { { "enlargegrid"} },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Enlargegrid> registration = RegisterEntry<Enlargegrid>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -125,9 +136,8 @@ class ModuleEnlargegrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("grid description file or name");
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
@@ -185,8 +195,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto missval1 = vlistInqVarMissval(vlistID1, varID);
 
@@ -194,9 +204,9 @@ public:
             for (size_t i = 0; i < gridsize1; ++i)
               if (gindex[i] >= 0) array2[gindex[i]] = array1[i];
 
-            nmiss = varray_num_mv(gridsize2, array2, missval1);
+            numMissVals = varray_num_mv(gridsize2, array2, missval1);
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
@@ -208,18 +218,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Enlargegrid(void *process)
-{
-  ModuleEnlargegrid enlargegrid;
-  enlargegrid.init(process);
-  enlargegrid.run();
-  enlargegrid.close();
-
-  return nullptr;
-}
diff --git a/src/Ensstat.cc b/src/Ensstat.cc
index 62e911db5966abc1636ad6d6c479a9a075cdcc9e..6db973f17a0e45fc48a4352fd2f02b6873236646 100644
--- a/src/Ensstat.cc
+++ b/src/Ensstat.cc
@@ -78,7 +78,7 @@ ensstat_func(void *ensarg)
 
   auto hasMissvals = false;
   for (int fileID = 0; fileID < nfiles; ++fileID)
-    if (ef[fileID].field[t].nmiss > 0) hasMissvals = true;
+    if (ef[fileID].field[t].numMissVals > 0) hasMissvals = true;
 
   auto varID = arg->varID[t];
   auto gridsize = ef[0].varList[varID].gridsize;
@@ -95,7 +95,7 @@ ensstat_func(void *ensarg)
 
       auto &field = fields[ompthID];
       field.missval = missval;
-      field.nmiss = 0;
+      field.numMissVals = 0;
       if (memType == MemType::Float)
         for (int fileID = 0; fileID < nfiles; ++fileID) field.vec_d[fileID] = ef[fileID].field[t].vec_f[i];
       else
@@ -107,7 +107,7 @@ ensstat_func(void *ensarg)
             if (dbl_is_equal(field.vec_d[fileID], ef[fileID].missval[t]))
               {
                 field.vec_d[fileID] = missval;
-                field.nmiss++;
+                field.numMissVals++;
               }
           }
 
@@ -115,13 +115,13 @@ ensstat_func(void *ensarg)
 
       if (dbl_is_equal(array2[i], field.missval)) atomicNumMiss++;
 
-      if (withCountData) count2[i] = nfiles - field.nmiss;
+      if (withCountData) count2[i] = nfiles - field.numMissVals;
     }
 
-  size_t nmiss = atomicNumMiss;
+  size_t numMissVals = atomicNumMiss;
 
   cdo_def_record(arg->streamID2, arg->varID[t], arg->levelID[t]);
-  cdo_write_record(arg->streamID2, array2, nmiss);
+  cdo_write_record(arg->streamID2, array2, numMissVals);
 
   if (withCountData)
     {
@@ -132,29 +132,32 @@ ensstat_func(void *ensarg)
   return nullptr;
 }
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("ensrange",  FieldFunc_Range,  0, nullptr);
-  cdo_operator_add("ensmin",    FieldFunc_Min,    0, nullptr);
-  cdo_operator_add("ensmax",    FieldFunc_Max,    0, nullptr);
-  cdo_operator_add("enssum",    FieldFunc_Sum,    0, nullptr);
-  cdo_operator_add("ensmean",   FieldFunc_Mean,   0, nullptr);
-  cdo_operator_add("ensavg",    FieldFunc_Avg,    0, nullptr);
-  cdo_operator_add("ensstd",    FieldFunc_Std,    0, nullptr);
-  cdo_operator_add("ensstd1",   FieldFunc_Std1,   0, nullptr);
-  cdo_operator_add("ensvar",    FieldFunc_Var,    0, nullptr);
-  cdo_operator_add("ensvar1",   FieldFunc_Var1,   0, nullptr);
-  cdo_operator_add("ensskew",   FieldFunc_Skew,   0, nullptr);
-  cdo_operator_add("enskurt",   FieldFunc_Kurt,   0, nullptr);
-  cdo_operator_add("ensmedian", FieldFunc_Median, 0, nullptr);
-  cdo_operator_add("enspctl",   FieldFunc_Pctl,   0, nullptr);
-  // clang-format on
-}
-
-class ModuleEnsstat
+class Ensstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ensstat",
+    .operators = { { "ensrange", FieldFunc_Range, 0, EnsstatHelp },
+                   { "ensmin", FieldFunc_Min, 0, EnsstatHelp },
+                   { "ensmax", FieldFunc_Max, 0, EnsstatHelp },
+                   { "enssum", FieldFunc_Sum, 0, EnsstatHelp },
+                   { "ensmean", FieldFunc_Mean, 0, EnsstatHelp },
+                   { "ensavg", FieldFunc_Avg, 0, EnsstatHelp },
+                   { "ensvar", FieldFunc_Var, 0, EnsstatHelp },
+                   { "ensvar1", FieldFunc_Var1, 0, EnsstatHelp },
+                   { "ensstd", FieldFunc_Std, 0, EnsstatHelp },
+                   { "ensstd1", FieldFunc_Std1, 0, EnsstatHelp },
+                   { "ensskew", FieldFunc_Skew, 0, EnsstatHelp },
+                   { "enskurt", FieldFunc_Kurt, 0, EnsstatHelp },
+                   { "ensmedian", FieldFunc_Median, 0, EnsstatHelp },
+                   { "enspctl", FieldFunc_Pctl, 0, EnsstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ensstat> registration = RegisterEntry<Ensstat>(module);
   cdo::Task task;
   int nrecs0;
 
@@ -175,11 +178,8 @@ class ModuleEnsstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
 
     auto operatorID = cdo_operator_id();
     auto operfunc = cdo_operator_f1(operatorID);
@@ -338,10 +338,7 @@ public:
                 task.start(ensstat_func, &ensstatArg);
                 t = !t;
               }
-            else
-              {
-                ensstat_func(&ensstatArg);
-              }
+            else { ensstat_func(&ensstatArg); }
           }
 
         if (Options::CDO_task) task.wait();
@@ -360,18 +357,5 @@ public:
     for (int fileID = 0; fileID < nfiles; ++fileID) cdo_stream_close(ef[fileID].streamID);
 
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Ensstat(void *process)
-{
-  ModuleEnsstat ensstat;
-  ensstat.init(process);
-  ensstat.run();
-  ensstat.close();
-
-  return nullptr;
-}
diff --git a/src/Ensstat3.cc b/src/Ensstat3.cc
index e484d0267108870ed2ff062ddcfaba3056f65236..08dad8cb093b0b2babaca08b5020f56df68e3c83 100644
--- a/src/Ensstat3.cc
+++ b/src/Ensstat3.cc
@@ -69,11 +69,30 @@ roc_curve_integrate(const Varray2D<double> &roc, int n)
   return area - 0.5;
 }
 
-class ModuleEnsstat3
+class Ensstat3 : public Process
 {
+  enum
+  {
+    func_roc,
+    func_rank
+  };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ensstat3",
+    .operators = { { "ensrkhistspace", func_rank, space_data, Ensstat2Help },
+                   { "ensrkhisttime", func_rank, time_data, Ensstat2Help },
+                   { "ensroc", func_roc, 0, Ensstat2Help } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ensstat3> registration = RegisterEntry<Ensstat3>(module);
   int j;
   int nrecs = 0, nrecs0;
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   int chksum = 0;  // for check of histogram population
   int levelID, varID, binID = 0;
   int gridID, gridID2;
@@ -87,12 +106,6 @@ class ModuleEnsstat3
     int vlistID;
   };
 
-  enum
-  {
-    func_roc,
-    func_rank
-  };
-
   std::vector<ens_file_t> ef;
   int nfiles;
   int operfunc;
@@ -119,14 +132,10 @@ class ModuleEnsstat3
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  cdo_operator_add("ensroc",         func_roc,  0,          nullptr);
-  cdo_operator_add("ensrkhistspace", func_rank, space_data, nullptr);
-  cdo_operator_add("ensrkhisttime",  func_rank, time_data,  nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -299,20 +308,20 @@ public:
         for (int recID = 0; recID < nrecs0; ++recID)
           {
 #ifdef _OPENMP
-#pragma omp parallel for default(none) shared(ef, nfiles) private(nmiss) lastprivate(varID, levelID)
+#pragma omp parallel for default(none) shared(ef, nfiles) private(numMissVals) lastprivate(varID, levelID)
 #endif
             for (int fileID = 0; fileID < nfiles; ++fileID)
               {
                 auto streamID = ef[fileID].streamID;
                 cdo_inq_record(streamID, &varID, &levelID);
-                cdo_read_record(streamID, ef[fileID].array.data(), &nmiss);
+                cdo_read_record(streamID, ef[fileID].array.data(), &numMissVals);
               }
 
             gridID = vlistInqVarGrid(vlistID1, varID);
             gridsize = gridInqSize(gridID);
             auto missval = vlistInqVarMissval(vlistID1, varID);
 
-            nmiss = 0;
+            numMissVals = 0;
             if (datafunc == TIME && operfunc == func_rank)
               for (binID = 0; binID < nfiles; binID++) array2[binID][0] = 0;
 
@@ -324,7 +333,7 @@ public:
                 auto ompthID = cdo_omp_get_thread_num();
 
                 field[ompthID].missval = missval;
-                field[ompthID].nmiss = 0;
+                field[ompthID].numMissVals = 0;
                 have_miss = 0;
                 for (int fileID = 0; fileID < nfiles; ++fileID)
                   {
@@ -422,7 +431,7 @@ public:
                     double val = (double) array2[binID][0];
                     //		fprintf(stderr,"%i ",(int)val);
                     cdo_def_record(streamID2, varIDs2[varID], binID);
-                    cdo_write_record(streamID2, &val, nmiss);
+                    cdo_write_record(streamID2, &val, numMissVals);
                   }
                 // fprintf(stderr,"\n");
               }
@@ -465,7 +474,7 @@ public:
             for (int i = 0; i < osize; ++i) tmpdoub[i] = (double) array2[binID][i];
 
             cdo_def_record(streamID2, varIDs2[varID], binID);
-            cdo_write_record(streamID2, tmpdoub.data(), nmiss);
+            cdo_write_record(streamID2, tmpdoub.data(), numMissVals);
           }
       }
     else if (operfunc == func_roc)
@@ -498,18 +507,5 @@ public:
     for (int fileID = 0; fileID < nfiles; ++fileID) cdo_stream_close(ef[fileID].streamID);
 
     if (operfunc != func_roc) cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-void *
-Ensstat3(void *process)
-{
-  ModuleEnsstat3 ensstat3;
-
-  ensstat3.init(process);
-  ensstat3.run();
-  ensstat3.close();
-
-  return nullptr;
-}
diff --git a/src/Ensval.cc b/src/Ensval.cc
index 47e04fb71e3f1c1fdedebc7f05ab69d6727234e9..7732a4f431e677d5d802e1dc3f36f74bfab0a865 100644
--- a/src/Ensval.cc
+++ b/src/Ensval.cc
@@ -15,8 +15,6 @@
      Score for Ensemble Prediction Systems, in: Weather and Forecasting (15) pp. 559-570
 */
 
-#include <algorithm>  // sort
-
 #include <cdi.h>
 
 #include "process_int.h"
@@ -47,11 +45,22 @@ enum RESTYPE_CRPS
   CRPS_POT
 };
 
-class ModuleEnsval
+class Ensval : public Process
 {
-  int k;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ensval",
+    .operators = { { "enscrps", CRPS, 0, EnsvalHelp }, { "ensbrs", BRS, 0, EnsvalHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, OBASE, NoRestriction },
+  };
+  inline static RegisterEntry<Ensval> registration = RegisterEntry<Ensval>(module);
+
   int nrecs = 0, nostreams = 0, ngrids;
-  size_t nmiss;
+  size_t numMissVals;
   int levelID = -1, varID = -1;
   size_t gridsize = 0;
   int vlistID;
@@ -98,13 +107,8 @@ class ModuleEnsval
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("enscrps", CRPS, 0, nullptr);
-    cdo_operator_add("ensbrs", BRS, 0, nullptr);
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -208,8 +212,7 @@ public:
         auto ofilename = ofilebase + string_format(".%s", typeSuffix);
         if (ofilename.size() > 0) ofilename += fileSuffix;
 
-        if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename)
-            && !FileUtils::user_file_overwrite(ofilename))
+        if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename) && !FileUtils::user_file_overwrite(ofilename))
           cdo_abort("Outputfile %s already exists!", ofilename);
 
         streamID2[stream] = cdo_open_write(ofilename.c_str());
@@ -228,6 +231,7 @@ public:
 
     varList_init(varList1, vlistID1);
   }
+
   void
   run()
   {
@@ -265,7 +269,7 @@ public:
 
                 ef[fileID].array.resize(gridsize);
 
-                cdo_read_record(streamID, ef[fileID].array.data(), &nmiss);
+                cdo_read_record(streamID, ef[fileID].array.data(), &numMissVals);
               }
 
             // xsize = gridInqXsize(gridID);
@@ -280,11 +284,11 @@ public:
             else
             */
             {
-              varray_fill(weights, 1.0 / gridsize);
+              ranges::fill(weights, 1.0 / gridsize);
               sum_weights = 1.0;
             }
 
-            nmiss = 0;
+            numMissVals = 0;
             heavyside0 = 0;
             heavysideN = 0;
 
@@ -308,7 +312,7 @@ public:
                   }
 
                 auto &x = val;
-                std::sort(x.begin(), x.end());  // Sort The Ensemble Array to ascending order
+                ranges::sort(x);  // Sort The Ensemble Array to ascending order
 
                 // only process if no missing value in ensemble
                 if (!have_miss && operfunc == CRPS)
@@ -326,7 +330,7 @@ public:
                       }
 
                     // Loop start at zero ==> 1st ensemble (c-indexing)
-                    for (k = 0; k < nens - 1; ++k)
+                    for (int k = 0; k < nens - 1; ++k)
                       {                     // Cumulate alpha and beta
                         if (xa > x[k + 1])  // left of heavyside
                           alpha[k + 1] += (x[k + 1] - x[k]) * weights[i];
@@ -349,7 +353,7 @@ public:
                     else if (x[nens - 1] < brs_thresh)
                       brs_g[nens] += weights[i];
                     else
-                      for (k = 0; k < nens - 1; ++k)
+                      for (int k = 0; k < nens - 1; ++k)
                         {
                           if (x[k + 1] >= brs_thresh && brs_thresh >= x[k])
                             {
@@ -366,7 +370,7 @@ public:
                         else if (x[nens - 1] < xa)
                           brs_o[nens] += weights[i];
                         else
-                          for (k = 0; k < nens - 1; ++k)
+                          for (int k = 0; k < nens - 1; ++k)
                             {
                               if (x[k + 1] >= xa && xa >= x[k])
                                 {
@@ -389,7 +393,7 @@ public:
                 crps = g * ((1. - o) * p * p + o * (1. - p) * (1. - p));
 
                 // Middle Bins
-                for (k = 1; k < nens; ++k)
+                for (int k = 1; k < nens; ++k)
                   {
                     p = (double) k / (double) nens;
 
@@ -430,7 +434,7 @@ public:
                 brs_uncty = 0;
 
                 double gsum = 0, obar = 0, osum = 0;
-                for (k = 0; k <= nens; ++k)
+                for (int k = 0; k <= nens; ++k)
                   {
                     obar += brs_g[k] * brs_o[k];
                     gsum += brs_g[k];
@@ -446,7 +450,7 @@ public:
                 double o = 0, p = 0, g = 0;
                 brs_uncty = obar * (1 - obar);
 
-                for (k = 0; k <= nens; ++k)
+                for (int k = 0; k <= nens; ++k)
                   {
 
                     g = brs_g[k];
@@ -489,14 +493,14 @@ public:
             switch (operfunc)
               {
               case (CRPS):
-                varray_fill(alpha, 0.0);
-                varray_fill(beta, 0.0);
+                ranges::fill(alpha, 0.0);
+                ranges::fill(beta, 0.0);
                 heavyside0 = 0;
                 heavysideN = 0;
                 break;
               case (BRS):
-                varray_fill(brs_o, 0.0);
-                varray_fill(brs_g, 0.0);
+                ranges::fill(brs_o, 0.0);
+                ranges::fill(brs_g, 0.0);
                 break;
               }
           }  // for ( int recID = 0; recID < nrecs; recID++ )
@@ -504,6 +508,7 @@ public:
       }
     while (nrecs);
   }
+
   void
   close()
   {
@@ -522,19 +527,5 @@ public:
       }
 
     gridDestroy(gridID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Ensval(void *process)
-{
-  ModuleEnsval ensval;
-
-  ensval.init(process);
-  ensval.run();
-  ensval.close();
-
-  return nullptr;
-}
diff --git a/src/Eof3d.cc b/src/Eof3d.cc
index 72b092b408d3bb963add26a6af04f470d5227e23..3ba52a16ece8156b6f78276860e2805cd42318ac 100644
--- a/src/Eof3d.cc
+++ b/src/Eof3d.cc
@@ -37,7 +37,7 @@
 
 // NO MISSING VALUE SUPPORT ADDED SO FAR
 
-class ModuleEOF3d
+class EOF3d : public Process
 {
   enum
   {
@@ -46,10 +46,24 @@ class ModuleEOF3d
     EOF3D_SPATIAL
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "EOF3d",
+    .operators = { { "eof3d", EOF3D_, 0, EOFsHelp },
+                   { "eof3dspatial", EOF3D_SPATIAL, 0, EOFsHelp },
+                   { "eof3dtime", EOF3D_TIME, 0, EOFsHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 2, OnlyFirst },
+  };
+  inline static RegisterEntry<EOF3d> registration = RegisterEntry<EOF3d>(module);
+
   size_t temp_size = 0, npack = 0;
   int varID, levelID;
   bool missval_warning = false;
-  size_t nmiss;
+  size_t numMissVals;
   int ngrids;
 
   int calendar = CALENDAR_STANDARD;
@@ -86,15 +100,10 @@ class ModuleEOF3d
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     // clang-format off
-  cdo_operator_add("eof3d",        EOF3D_,        0, nullptr);
-  cdo_operator_add("eof3dtime",    EOF3D_TIME,    0, nullptr);
-  cdo_operator_add("eof3dspatial", EOF3D_SPATIAL, 0, nullptr);
     // clang-format on
 
     const auto operatorID = cdo_operator_id();
@@ -225,7 +234,7 @@ public:
             const auto gridsize = varList1[varID].gridsize;
             const auto missval = varList1[varID].missval;
 
-            cdo_read_record(streamID1, in.data(), &nmiss);
+            cdo_read_record(streamID1, in.data(), &numMissVals);
 
             const auto offset = gridsize * levelID;
             for (size_t i = 0; i < gridsize; ++i)
@@ -440,16 +449,16 @@ public:
                 const auto offset = levelID * gridsizemax;
                 if (tsID < n_eig)
                   {
-                    nmiss = array_num_mv(gridsizemax, &eigenvectors[varID][tsID][offset], missval);
+                    numMissVals = array_num_mv(gridsizemax, &eigenvectors[varID][tsID][offset], missval);
                     cdo_def_record(streamID3, varID, levelID);
-                    cdo_write_record(streamID3, &eigenvectors[varID][tsID][offset], nmiss);
+                    cdo_write_record(streamID3, &eigenvectors[varID][tsID][offset], numMissVals);
                   }
               }
 
-            nmiss = (DBL_IS_EQUAL(eigenvalues[varID][tsID][0], missval)) ? 1 : 0;
+            numMissVals = (DBL_IS_EQUAL(eigenvalues[varID][tsID][0], missval)) ? 1 : 0;
 
             cdo_def_record(streamID2, varID, 0);
-            cdo_write_record(streamID2, eigenvalues[varID][tsID].data(), nmiss);
+            cdo_write_record(streamID2, eigenvalues[varID][tsID].data(), numMissVals);
           }  // for ( varID = 0; ... )
       }      // for ( tsID = 0; ... )
   }
@@ -460,17 +469,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-EOF3d(void *process)
-{
-  ModuleEOF3d eof3d;
-  eof3d.init(process);
-  eof3d.run();
-  eof3d.close();
-  return nullptr;
-}
diff --git a/src/Eofcoeff.cc b/src/Eofcoeff.cc
index 5d9c405c7b8c794614c82d53ef5e8392f375c90f..0488508dbbe567a306a636477c5fafdbdc32d0c2 100644
--- a/src/Eofcoeff.cc
+++ b/src/Eofcoeff.cc
@@ -21,12 +21,24 @@
 
 // NO MISSING VALUE SUPPORT ADDED SO FAR
 
-class ModuleEofcoeff
+class Eofcoeff : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Eofcoeff",
+    .operators = { { "eofcoeff", EofcoeffHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Eofcoeff> registration = RegisterEntry<Eofcoeff>(module);
+
   double missval1 = -999, missval2;
   int varID, levelID;
   int nrecs;
-  size_t nmiss;
+  size_t numMissVals;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -52,15 +64,8 @@ class ModuleEofcoeff
 
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
-
-    cdo_operator_add("eofcoeff", 0, 0, nullptr);
-
     streamID1 = cdo_open_read(0);
     streamID2 = cdo_open_read(1);
 
@@ -93,6 +98,7 @@ public:
 
     eofID = 0;
   }
+
   void
   run()
   {
@@ -109,13 +115,13 @@ public:
             eof[varID][levelID][eofID].grid = gridID1;
             eof[varID][levelID][eofID].missval = missval1;
             eof[varID][levelID][eofID].resize(gridsize);
-            varray_fill(gridsize, eof[varID][levelID][eofID].vec_d, missval1);
+            ranges::fill(eof[varID][levelID][eofID].vec_d, missval1);
 
             if (varID >= nvars) cdo_abort("Internal error - varID >= nvars");
             if (levelID >= nlevs) cdo_abort("Internal error - levelID >= nlevs");
 
-            cdo_read_record(streamID1, eof[varID][levelID][eofID].vec_d.data(), &nmiss);
-            eof[varID][levelID][eofID].nmiss = nmiss;
+            cdo_read_record(streamID1, eof[varID][levelID][eofID].vec_d.data(), &numMissVals);
+            eof[varID][levelID][eofID].numMissVals = numMissVals;
           }
         eofID++;
       }
@@ -159,7 +165,7 @@ public:
     in.resize(gridsize);
     in.grid = gridID1;
     out.missval = missval1;
-    out.nmiss = 0;
+    out.numMissVals = 0;
     out.resize(1);
 
     int tsID = 0;
@@ -179,7 +185,7 @@ public:
           {
             cdo_inq_record(streamID2, &varID, &levelID);
             missval2 = vlistInqVarMissval(vlistID2, varID);
-            cdo_read_record(streamID2, in.vec_d.data(), &in.nmiss);
+            cdo_read_record(streamID2, in.vec_d.data(), &in.numMissVals);
 
             for (eofID = 0; eofID < neof; eofID++)
               {
@@ -197,16 +203,16 @@ public:
                       }
                   }
                 if (!DBL_IS_EQUAL(out.vec_d[0], 0.))
-                  nmiss = 0;
+                  numMissVals = 0;
                 else
                   {
-                    nmiss = 1;
+                    numMissVals = 1;
                     out.vec_d[0] = missval2;
                   }
 
                 cdo_def_record(streamIDs[eofID], varID, levelID);
                 // fprintf(stderr, "%d %d %d %d %d %g\n", streamIDs[eofID],tsID, recID, varID, levelID,*out.vec_d.data());
-                cdo_write_record(streamIDs[eofID], out.vec_d.data(), nmiss);
+                cdo_write_record(streamIDs[eofID], out.vec_d.data(), numMissVals);
               }
             if (varID >= nvars) cdo_abort("Internal error - varID >= nvars");
             if (levelID >= nlevs) cdo_abort("Internal error - levelID >= nlevs");
@@ -215,6 +221,7 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
@@ -222,19 +229,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Eofcoeff(void *process)
-{
-  ModuleEofcoeff eofcoeff;
-
-  eofcoeff.init(process);
-  eofcoeff.run();
-  eofcoeff.close();
-
-  return nullptr;
-}
diff --git a/src/Eofcoeff3d.cc b/src/Eofcoeff3d.cc
index 5f1ba279f62dd68b65aa9d7e2a03fd91dcfd1d81..0768bf594337954a7935fab09cb7b3e35fc33f2d 100644
--- a/src/Eofcoeff3d.cc
+++ b/src/Eofcoeff3d.cc
@@ -21,12 +21,24 @@
 
 // NO MISSING VALUE SUPPORT ADDED SO FAR
 
-class ModuleEofcoeff3d
+class Eofcoeff3d : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Eofcoeff3d",
+    .operators = { { "eofcoeff3d", EofcoeffHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Eofcoeff3d> registration = RegisterEntry<Eofcoeff3d>(module);
+
   double missval1 = -999, missval2 = -999;
   int varID, levelID;
   int nrecs;
-  size_t nmiss;
+  size_t numMissVals;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -53,14 +65,8 @@ class ModuleEofcoeff3d
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
-
-    cdo_operator_add("eofcoeff3d", 0, 0, nullptr);
-
     streamID1 = cdo_open_read(0);
     streamID2 = cdo_open_read(1);
 
@@ -106,13 +112,13 @@ public:
             eof[varID][levelID][eofID].grid = gridID1;
             eof[varID][levelID][eofID].missval = missval1;
             eof[varID][levelID][eofID].resize(gridsize);
-            varray_fill(gridsize, eof[varID][levelID][eofID].vec_d, missval1);
+            ranges::fill(eof[varID][levelID][eofID].vec_d, missval1);
 
             if (varID >= nvars) cdo_abort("Internal error - varID >= nvars");
             if (levelID >= nlevs) cdo_abort("Internal error - levelID >= nlevs");
 
-            cdo_read_record(streamID1, eof[varID][levelID][eofID].vec_d.data(), &nmiss);
-            eof[varID][levelID][eofID].nmiss = nmiss;
+            cdo_read_record(streamID1, eof[varID][levelID][eofID].vec_d.data(), &numMissVals);
+            eof[varID][levelID][eofID].numMissVals = numMissVals;
           }
         eofID++;
       }
@@ -121,7 +127,7 @@ public:
 
     if (Options::cdoVerbose) cdo_print("%s contains %i eof's", cdo_get_stream_name(0), neof);
     // Create 1x1 Grid for output
-    const auto gridID3 = gridCreate(GRID_LONLAT, 1);
+    auto gridID3 = gridCreate(GRID_LONLAT, 1);
     gridDefXsize(gridID3, 1);
     gridDefYsize(gridID3, 1);
     double xvals = 0, yvals = 0;
@@ -129,7 +135,7 @@ public:
     gridDefYvals(gridID3, &yvals);
 
     double zvals = 0.;
-    const auto zaxisID3 = zaxisCreate(ZAXIS_GENERIC, 1);
+    auto zaxisID3 = zaxisCreate(ZAXIS_GENERIC, 1);
     zaxisDefLevels(zaxisID3, &zvals);
     cdiDefKeyString(zaxisID3, CDI_GLOBAL, CDI_KEY_NAME, "zaxis_Reduced");
     cdiDefKeyString(zaxisID3, CDI_GLOBAL, CDI_KEY_LONGNAME,
@@ -137,12 +143,12 @@ public:
 
     // Create var-list and time-axis for output
 
-    const auto vlistID3 = vlistDuplicate(vlistID2);
+    auto vlistID3 = vlistDuplicate(vlistID2);
 
-    const auto ngrids = vlistNgrids(vlistID3);
+    auto ngrids = vlistNgrids(vlistID3);
     for (int i = 0; i < ngrids; ++i) vlistChangeGridIndex(vlistID3, i, gridID3);
 
-    const auto nzaxis = vlistNzaxis(vlistID3);
+    auto nzaxis = vlistNzaxis(vlistID3);
     for (int i = 0; i < nzaxis; ++i) vlistChangeZaxisIndex(vlistID3, i, zaxisID3);
 
     vlistDefTaxis(vlistID3, taxisID3);
@@ -175,7 +181,7 @@ public:
         for (eofID = 0; eofID < neof; eofID++)
           {
             out[varID][eofID].missval = missval1;
-            out[varID][eofID].nmiss = 0;
+            out[varID][eofID].numMissVals = 0;
             out[varID][eofID].resize(1);
           }
       }
@@ -199,14 +205,14 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             cdo_inq_record(streamID2, &varID, &levelID);
-            cdo_read_record(streamID2, in.vec_d.data(), &in.nmiss);
+            cdo_read_record(streamID2, in.vec_d.data(), &in.numMissVals);
             missval2 = vlistInqVarMissval(vlistID2, varID);
 
             for (eofID = 0; eofID < neof; eofID++)
               {
                 if (recID == 0) cdo_def_timestep(streamIDs[eofID], tsID);
 
-                nmiss = 0;
+                numMissVals = 0;
                 for (size_t i = 0; i < gridsize; ++i)
                   {
                     if (!DBL_IS_EQUAL(in.vec_d[i], missval2) && !DBL_IS_EQUAL(eof[varID][levelID][eofID].vec_d[i], missval1))
@@ -215,11 +221,11 @@ public:
                         out[varID][eofID].vec_d[0] += in.vec_d[i] * eof[varID][levelID][eofID].vec_d[i];
                       }
                     else
-                      nmiss += 1;
+                      numMissVals += 1;
                   }
                 /*
-                if ( nmiss ) {
-                  out[varID][eofID].nmiss=1;
+                if ( numMissVals ) {
+                  out[varID][eofID].numMissVals=1;
                   out[varID][eofID].vec_d[0] = missval2;
                 }
                 */
@@ -234,7 +240,7 @@ public:
             for (varID = 0; varID < nvars; ++varID)
               {
                 cdo_def_record(streamIDs[eofID], varID, 0);
-                cdo_write_record(streamIDs[eofID], out[varID][eofID].vec_d.data(), out[varID][eofID].nmiss);
+                cdo_write_record(streamIDs[eofID], out[varID][eofID].vec_d.data(), out[varID][eofID].numMissVals);
               }
           }
 
@@ -249,18 +255,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Eofcoeff3d(void *process)
-{
-  ModuleEofcoeff3d eofcoeff3d;
-  eofcoeff3d.init(process);
-  eofcoeff3d.run();
-  eofcoeff3d.close();
-
-  return nullptr;
-}
diff --git a/src/EstFreq.cc b/src/EstFreq.cc
index 6cfc112e03135551170003dd1ce5e5fdef357dec..022bc532a5ece867ed60f2ccce93456d2d15ad0e 100644
--- a/src/EstFreq.cc
+++ b/src/EstFreq.cc
@@ -10,8 +10,19 @@
 #include "process_int.h"
 #include "cdo_options.h"
 
-class ModuleEstFreq
+class EstFreq : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "EstFreq",
+    .operators = { { "estfreq"} },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<EstFreq> registration = RegisterEntry<EstFreq>(module);
   int tsID = 0;
   int fyear = 0, lyear, fmonth = 0, lmonth, dummy;
   int step_per_year = 0, currentyear, currentmon;
@@ -34,11 +45,9 @@ class ModuleEstFreq
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
@@ -166,17 +175,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-EstFreq(void *process)
-{
-  ModuleEstFreq estFreq;
-  estFreq.init(process);
-  estFreq.run();
-  estFreq.close();
-  return nullptr;
-}
diff --git a/src/Exprf.cc b/src/Exprf.cc
index 822d604f3f4e81d3de5bda180c802c166d7d77b2..3ed3617b63a6ddbcb60e511e7cd68b2ca6e1b550 100644
--- a/src/Exprf.cc
+++ b/src/Exprf.cc
@@ -72,7 +72,7 @@ exprs_from_file(const std::vector<std::string> &exprArgv)
   return buffer.str();
 }
 
-#define MAX_PARAMS 4096
+constexpr int MaxParams = 4096;
 
 static std::size_t
 replace_all(std::string &inout, const std::string &what, const std::string &with)
@@ -160,7 +160,7 @@ params_init(std::vector<ParamEntry> &params, const VarList &varList, int vlistID
 static void
 params_delete(const std::vector<ParamEntry> &params)
 {
-  for (int varID = 0; varID < MAX_PARAMS; ++varID)
+  for (int varID = 0; varID < MaxParams; ++varID)
     {
       if (params[varID].data) delete[] params[varID].data;
     }
@@ -256,8 +256,8 @@ parse_param_init(ParseParamType &parseArg, int vlistID, int pointID, int zonalID
   auto nzaxis = vlistNzaxis(vlistID);
   auto maxCoords = ngrids * 5 + nzaxis * 3;
 
-  parseArg.maxparams = MAX_PARAMS;
-  parseArg.params.resize(MAX_PARAMS);
+  parseArg.maxparams = MaxParams;
+  parseArg.params.resize(MaxParams);
   parseArg.nparams = nvars;
   parseArg.cnparams = nvars;
   parseArg.nvars1 = nvars;
@@ -327,19 +327,23 @@ set_date_and_time(ParamEntry &varts, int calendar, int tsID, const CdiDateTime &
   varts.data[CoordIndex::DPY] = days_per_year(calendar, year);
 }
 
-static void
-addOperators(void)
+class Exprf : public Process
 {
-  // clang-format off
-  cdo_operator_add("expr",   1, 1, "expressions");
-  cdo_operator_add("exprf",  1, 0, "expr script filename");
-  cdo_operator_add("aexpr",  0, 1, "expressions");
-  cdo_operator_add("aexprf", 0, 0, "expr script filename");
-  // clang-format on
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Exprf",
+    .operators = { { "expr", 1, 1, "expressions", ExprHelp },
+                   { "exprf", 1, 0, "exprscriptfilename", ExprHelp },
+                   { "aexpr", 0, 1, "expressions", ExprHelp },
+                   { "aexprf", 0, 0, "exprscriptfilename", ExprHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Exprf> registration = RegisterEntry<Exprf>(module);
 
-class ModuleExprf
-{
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -366,12 +370,8 @@ class ModuleExprf
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
-
     auto operatorID = cdo_operator_id();
     bool replacesVariables = cdo_operator_f1(operatorID);
     bool readsCommandLine = cdo_operator_f2(operatorID);
@@ -564,7 +564,7 @@ public:
                   }
                 else if (coord == 'd')
                   {
-                    varray_fill(nlev, cdata, 1.0);
+                    ranges::fill(cdata, 1.0);
                     if (zaxisInqLbounds(zaxisID, nullptr) && zaxisInqUbounds(zaxisID, nullptr))
                       {
                         std::vector<double> lbounds(nlev), ubounds(nlev);
@@ -646,7 +646,7 @@ public:
         // for (int varID = 0; varID < nvars1; ++varID) printf(">>> %s %d\n", params[varID].name.c_str(), params[varID].isValid);
         for (int varID = 0; varID < nvars1; ++varID) params[varID].isValid = true;
         for (int varID = 0; varID < nvars1; ++varID)
-          if (tsID == 0 || params[varID].steptype != TIME_CONSTANT) params[varID].nmiss = 0;
+          if (tsID == 0 || params[varID].steptype != TIME_CONSTANT) params[varID].numMissVals = 0;
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
@@ -656,11 +656,11 @@ public:
               {
                 auto offset = params[varID].ngp * levelID;
                 auto vardata = params[varID].data + offset;
-                size_t nmiss;
-                cdo_read_record(streamID1, vardata, &nmiss);
-                params[varID].nmiss += nmiss;
+                size_t numMissVals;
+                cdo_read_record(streamID1, vardata, &numMissVals);
+                params[varID].numMissVals += numMissVals;
 
-                if (nmiss > 0) cdo_check_missval(params[varID].missval, params[varID].name);
+                if (numMissVals > 0) cdo_check_missval(params[varID].missval, params[varID].name);
               }
           }
 
@@ -669,8 +669,9 @@ public:
             auto pidx = varIDmap[varID];
             if (pidx < nvars1) continue;
 
-            params[pidx].nmiss = 0;
-            varray_fill(params[pidx].ngp * params[pidx].nlev, params[pidx].data, 0.0);
+            auto &param = params[pidx];
+            param.numMissVals = 0;
+            ranges::fill_n(param.data, param.ngp * param.nlev, 0.0);
           }
 
         parseArg.cnparams = vartsID + 1;
@@ -698,9 +699,9 @@ public:
               {
                 auto offset = ngp * levelID;
                 double *vardata = params[pidx].data + offset;
-                auto nmiss = array_num_mv(ngp, vardata, missval);
+                auto numMissVals = array_num_mv(ngp, vardata, missval);
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, vardata, nmiss);
+                cdo_write_record(streamID2, vardata, numMissVals);
               }
           }
 
@@ -720,18 +721,5 @@ public:
 
     gridDestroy(pointID);
     if (zonalID != -1) gridDestroy(zonalID);
-
-    cdo_finish();
   }
 };
-
-void *
-Exprf(void *process)
-{
-  ModuleExprf expr;
-  expr.init(process);
-  expr.run();
-  expr.close();
-
-  return nullptr;
-}
diff --git a/src/FC.cc b/src/FC.cc
index 4e52c0ef23e527139315eec23b019c5fbb94f876..c9d9725d9bc3c501e0978605415903d209bc349d 100644
--- a/src/FC.cc
+++ b/src/FC.cc
@@ -47,8 +47,25 @@ vlistGetFirstReg2DGrid(int vlistID)
   return -1;
 }
 
-class ModuleFC
+class FC : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "FC",
+    .operators = { { "fc2sp"},
+                   { "sp2fc"},
+                   { "fc2gp"},
+                   { "gp2fc"},
+                   { "fourier2grid", 1, 0, nullptr},
+                   { "grid2fourier", 1, 0, nullptr} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<FC> registration = RegisterEntry<FC>(module);
+  int FC2SP, SP2FC, FC2GP, GP2FC, GRID2FOURIER, FOURIER2GRID;
   int gridID1 = -1, gridID2 = -1;
   size_t nlon = 0, nlat = 0;
   int ntr = 0;
@@ -73,24 +90,21 @@ class ModuleFC
   FC_Transformation fcTrans;
   SP_Transformation spTrans;
 
-  int FC2SP, SP2FC, FC2GP, GP2FC, GRID2FOURIER, FOURIER2GRID;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
 
-    FC2SP = cdo_operator_add("fc2sp", 0, 0, nullptr);
-    SP2FC = cdo_operator_add("sp2fc", 0, 0, nullptr);
-    FC2GP = cdo_operator_add("fc2gp", 0, 0, nullptr);
-    GP2FC = cdo_operator_add("gp2fc", 0, 0, nullptr);
-    GRID2FOURIER = cdo_operator_add("grid2fourier", 1, 0, nullptr);
-    FOURIER2GRID = cdo_operator_add("fourier2grid", 1, 0, nullptr);
+    FC2SP = module.get_id("fc2sp");
+    SP2FC = module.get_id("sp2fc");
+    FC2GP = module.get_id("fc2gp");
+    GP2FC = module.get_id("gp2fc");
+    GRID2FOURIER = module.get_id("grid2fourier");
+    FOURIER2GRID = module.get_id("fourier2grid");
 
     operatorID = cdo_operator_id();
     auto operfunc = cdo_operator_f1(operatorID);
@@ -313,9 +327,9 @@ public:
 
             if (vars[varID])
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array1.data(), &nmiss);
-                if (nmiss) cdo_abort("Missing values unsupported for spectral/fourier data!");
+                size_t numMissVals;
+                cdo_read_record(streamID1, array1.data(), &numMissVals);
+                if (numMissVals) cdo_abort("Missing values unsupported for spectral/fourier data!");
 
                 gridID1 = vlistInqVarGrid(vlistID1, varID);
                 if (operatorID == FC2SP)
@@ -331,7 +345,7 @@ public:
                 else if (operatorID == GRID2FOURIER)
                   grid2fourier(gridID1, array1, gridID2, array2);
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, array2.data(), nmiss);
+                cdo_write_record(streamID2, array2.data(), numMissVals);
               }
             else
               {
@@ -339,9 +353,9 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    size_t nmiss;
-                    cdo_read_record(streamID1, array1.data(), &nmiss);
-                    cdo_write_record(streamID2, array1.data(), nmiss);
+                    size_t numMissVals;
+                    cdo_read_record(streamID1, array1.data(), &numMissVals);
+                    cdo_write_record(streamID2, array1.data(), numMissVals);
                   }
               }
           }
@@ -355,19 +369,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-FC(void *process)
-{
-  ModuleFC fc;
-
-  fc.init(process);
-  fc.run();
-  fc.close();
-
-  return nullptr;
-}
diff --git a/src/Filedes.cc b/src/Filedes.cc
index a2c01833c06b717682114f5a74cb49923733394d..7fedd603db48426419d963049e7a9222a268f79b 100644
--- a/src/Filedes.cc
+++ b/src/Filedes.cc
@@ -305,8 +305,30 @@ filedes(CdoStreamID streamID)
   printf("\n");
 }
 
-class ModuleFiledes
+class Filedes : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Filedes",
+    .operators = { { "filedes", FiledesHelp },
+                   { "griddes", FiledesHelp },
+                   { "griddes2", FiledesHelp },
+                   { "zaxisdes", FiledesHelp },
+                   { "vct", FiledesHelp },
+                   { "vct2", FiledesHelp },
+                   { "codetab", FiledesHelp },
+                   { "vlist", FiledesHelp },
+                   { "partab", FiledesHelp },
+                   { "partab2", FiledesHelp },
+                   { "spartab", FiledesHelp } },
+    .aliases = { { "vardes", "codetab" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Filedes> registration = RegisterEntry<Filedes>(module);
+  int GRIDDES, GRIDDES2, ZAXISDES, VCT, VCT2, CODETAB, FILEDES, VLIST, SPARTAB, PARTAB, PARTAB2;
   int operatorID;
 
   CdoStreamID streamID;
@@ -315,26 +337,23 @@ class ModuleFiledes
 
   VarList varList;
 
-  int GRIDDES, GRIDDES2, ZAXISDES, VCT, VCT2, CODETAB, FILEDES, VLIST, SPARTAB, PARTAB, PARTAB2;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    GRIDDES  = cdo_operator_add("griddes",   0, 0, nullptr);
-    GRIDDES2 = cdo_operator_add("griddes2",  0, 0, nullptr);
-    ZAXISDES = cdo_operator_add("zaxisdes",  0, 0, nullptr);
-    VCT      = cdo_operator_add("vct",       0, 0, nullptr);
-    VCT2     = cdo_operator_add("vct2",      0, 0, nullptr);
-    CODETAB  = cdo_operator_add("codetab",   0, 0, nullptr);
-    FILEDES  = cdo_operator_add("filedes",   0, 0, nullptr);
-    VLIST    = cdo_operator_add("vlist",     0, 0, nullptr);
-    SPARTAB  = cdo_operator_add("spartab",   0, 0, nullptr);
-    PARTAB   = cdo_operator_add("partab",    0, 0, nullptr);
-    PARTAB2  = cdo_operator_add("partab2",   0, 0, nullptr);
+GRIDDES = module.get_id("griddes");
+GRIDDES2 = module.get_id("griddes2");
+ZAXISDES = module.get_id("zaxisdes");
+VCT = module.get_id("vct");
+VCT2 = module.get_id("vct2");
+CODETAB = module.get_id("codetab");
+FILEDES = module.get_id("filedes");
+VLIST = module.get_id("vlist");
+SPARTAB = module.get_id("spartab");
+PARTAB = module.get_id("partab");
+PARTAB2 = module.get_id("partab2");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -386,19 +405,5 @@ public:
     cdo_stream_close(streamID);
 
     if (!cdo::stdoutIsTerminal) Options::silentMode = true;
-
-    cdo_finish();
   }
 };
-
-void *
-Filedes(void *process)
-{
-  ModuleFiledes filedes;
-
-  filedes.init(process);
-  filedes.run();
-  filedes.close();
-
-  return nullptr;
-}
diff --git a/src/Fillmiss.cc b/src/Fillmiss.cc
index 94b037f42eb2e4bd5eda07bfffa7a6d731241c84..0eac20c544104b65a9aa96b7749d4174ede5fb46 100644
--- a/src/Fillmiss.cc
+++ b/src/Fillmiss.cc
@@ -281,12 +281,12 @@ fillmiss_one_step(Field &field1, Field &field2, int maxfill)
 
 template <typename T>
 void
-setmisstodis(size_t nmiss, int gridID, Varray<T> &vIn, Varray<T> &vOut, T missval, int numNeighbors)
+setmisstodis(size_t numMissVals, int gridID, Varray<T> &vIn, Varray<T> &vOut, T missval, int numNeighbors)
 {
   auto gridID0 = gridID;
 
   auto gridsize = gridInqSize(gridID);
-  auto nvals = gridsize - nmiss;
+  auto nvals = gridsize - numMissVals;
   gridID = generate_full_point_grid(gridID);
 
   if (!gridHasCoordinates(gridID)) cdo_abort("Cell center coordinates missing!");
@@ -307,7 +307,7 @@ setmisstodis(size_t nmiss, int gridID, Varray<T> &vIn, Varray<T> &vOut, T missva
   cdo_grid_to_radian(gridID, CDI_XAXIS, xvals, "grid center lon");
   cdo_grid_to_radian(gridID, CDI_YAXIS, yvals, "grid center lat");
 
-  std::vector<size_t> mindex(nmiss, 1), vindex(nvals, 1);
+  std::vector<size_t> mindex(numMissVals, 1), vindex(nvals, 1);
   Varray<double> lons(nvals), lats(nvals);
 
   size_t nv = 0, nm = 0;
@@ -340,7 +340,7 @@ setmisstodis(size_t nmiss, int gridID, Varray<T> &vIn, Varray<T> &vOut, T missva
 
   GridPointSearch gps;
 
-  if (nmiss)
+  if (numMissVals)
     {
       auto xIsCyclic = false;
       size_t dims[2] = { nvals, 0 };
@@ -359,10 +359,10 @@ setmisstodis(size_t nmiss, int gridID, Varray<T> &vIn, Varray<T> &vOut, T missva
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
 #endif
-  for (size_t i = 0; i < nmiss; ++i)
+  for (size_t i = 0; i < numMissVals; ++i)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / nmiss);
+      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / numMissVals);
 
       auto ompthID = cdo_omp_get_thread_num();
 
@@ -373,7 +373,8 @@ setmisstodis(size_t nmiss, int gridID, Varray<T> &vIn, Varray<T> &vOut, T missva
       if (nadds)
         {
           double result = 0.0;
-          for (size_t n = 0; n < nadds; ++n) result += vIn[vindex[knnWeights[ompthID].m_indices[n]]] * knnWeights[ompthID].m_dist[n];
+          for (size_t n = 0; n < nadds; ++n)
+            result += vIn[vindex[knnWeights[ompthID].m_indices[n]]] * knnWeights[ompthID].m_dist[n];
           vOut[mindex[i]] = result;
         }
     }
@@ -393,13 +394,28 @@ setmisstodis(Field &field1, Field &field2, int numNeighbors)
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    setmisstodis(field1.nmiss, field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, numNeighbors);
+    setmisstodis(field1.numMissVals, field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, numNeighbors);
   else
-    setmisstodis(field1.nmiss, field1.grid, field1.vec_d, field2.vec_d, field1.missval, numNeighbors);
+    setmisstodis(field1.numMissVals, field1.grid, field1.vec_d, field2.vec_d, field1.missval, numNeighbors);
 }
 
-class ModuleFillmiss
+class Fillmiss : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Fillmiss",
+    .operators = { { "fillmiss", 0, 0, "nfill"},
+                   { "fillmiss2", 0, 0, "nfill"},
+                   { "setmisstonn", 0, 0, "", SetmissHelp },
+                   { "setmisstodis", 0, 0, "numberofneighbors", SetmissHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Fillmiss> registration = RegisterEntry<Fillmiss>(module);
+  int FILLMISS, FILLMISS2, SETMISSTONN, SETMISSTODIS;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -413,19 +429,17 @@ class ModuleFillmiss
   Field field1, field2;
 
   void (*fill_method)(Field &, Field &, int) = &setmisstodis;
-  int FILLMISS, FILLMISS2, SETMISSTONN, SETMISSTODIS;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    FILLMISS        = cdo_operator_add("fillmiss"   ,   0, 0, "nfill");
-    FILLMISS2 = cdo_operator_add("fillmiss2"  ,   0, 0, "nfill");
-    SETMISSTONN     = cdo_operator_add("setmisstonn" ,  0, 0, "");
-    SETMISSTODIS    = cdo_operator_add("setmisstodis" , 0, 0, "number of neighbors");
+FILLMISS = module.get_id("fillmiss");
+FILLMISS2 = module.get_id("fillmiss2");
+SETMISSTONN = module.get_id("setmisstonn");
+SETMISSTODIS = module.get_id("setmisstodis");
 
     operatorID = cdo_operator_id();
 
@@ -484,19 +498,18 @@ public:
 
             cdo_def_record(streamID2, varID, levelID);
 
-            if (field1.nmiss == 0 || field1.nmiss == varList1[varID].gridsize) { cdo_write_record(streamID2, field1); }
+            if (field1.numMissVals == 0 || field1.numMissVals == varList1[varID].gridsize) { cdo_write_record(streamID2, field1); }
             else
               {
                 auto gridtype = gridInqType(varList1[varID].gridID);
-                if ((operatorID == FILLMISS || operatorID == FILLMISS2)
-                    && (gridtype == GRID_GME || gridtype == GRID_UNSTRUCTURED))
+                if ((operatorID == FILLMISS || operatorID == FILLMISS2) && (gridtype == GRID_GME || gridtype == GRID_UNSTRUCTURED))
                   cdo_abort("%s data unsupported!", gridNamePtr(gridtype));
 
                 field2.init(varList1[varID]);
 
                 fill_method(field1, field2, nfill);
 
-                field2.nmiss = field_num_mv(field2);
+                field2.numMissVals = field_num_mv(field2);
 
                 cdo_write_record(streamID2, field2);
               }
@@ -511,18 +524,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Fillmiss(void *process)
-{
-  ModuleFillmiss fillmiss;
-  fillmiss.init(process);
-  fillmiss.run();
-  fillmiss.close();
-
-  return nullptr;
-}
diff --git a/src/Filter.cc b/src/Filter.cc
index f8188eeb68a4cbea5af792e3eeb3dd9978727005..f0510deb05475dc3633c86f94e9701bac37783a6 100644
--- a/src/Filter.cc
+++ b/src/Filter.cc
@@ -20,6 +20,8 @@
 
 #ifdef HAVE_LIBFFTW3
 #include <fftw3.h>
+#include <mutex>
+static std::mutex fftwMutex;
 #endif
 
 #include "cdi.h"
@@ -34,6 +36,36 @@
 #include "cimdOmp.h"
 #include "field_functions.h"
 
+size_t
+vars_numArrays(const FieldVector3D &vars)
+{
+  size_t numArrays{ 0 };
+  for (size_t i = 0; i < vars[0].size(); ++i)
+    {
+      for (size_t j = 0; j < vars[0][i].size(); ++j) numArrays++;
+    }
+
+  return numArrays;
+}
+
+size_t
+vars_allocatedMem(const FieldVector3D &vars)
+{
+  size_t allocatedMem{ 0 };
+  for (size_t i = 0; i < vars[0].size(); ++i)
+    {
+      for (size_t j = 0; j < vars[0][i].size(); ++j)
+        {
+          if (vars[0][i][j].memType == MemType::Float)
+            allocatedMem += vars[0][i][j].vec_f.size() * 4;
+          else
+            allocatedMem += vars[0][i][j].vec_d.size() * 8;
+        }
+    }
+
+  return allocatedMem;
+}
+
 static void
 create_fmasc(int nts, double fdata, double fmin, double fmax, std::vector<int> &fmasc)
 {
@@ -88,7 +120,7 @@ struct FilterMemory
 #endif
 };
 
-class ModuleFilter
+class Filter : public Process
 {
   enum
   {
@@ -97,6 +129,22 @@ class ModuleFilter
     LOWPASS
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Filter",
+    // clang-format off
+    .operators = { { "bandpass", BANDPASS, 0, FilterHelp },
+                   { "highpass", HIGHPASS, 0, FilterHelp },
+                   { "lowpass", LOWPASS, 0, FilterHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Filter> registration = RegisterEntry<Filter>(module);
+
   std::vector<std::string> tunits;
   std::vector<int> iunits;
   int year0, month0, day0;
@@ -113,8 +161,6 @@ class ModuleFilter
   int vlistID1;
   int vlistID2;
 
-  FieldVector3D vars;
-
   VarList varList;
 
   bool useFFTW = false;
@@ -125,17 +171,11 @@ class ModuleFilter
 
 public:
   void
-  init(void *process)
+  init()
   {
     tunits = { "second", "minute", "hour", "day", "month", "year" };
     iunits = { 31536000, 525600, 8760, 365, 12, 1 };
 
-    cdo_initialize(process);
-
-    cdo_operator_add("bandpass", BANDPASS, 0, nullptr);
-    cdo_operator_add("highpass", HIGHPASS, 0, nullptr);
-    cdo_operator_add("lowpass", LOWPASS, 0, nullptr);
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -166,9 +206,12 @@ public:
 
     nvars = vlistNvars(vlistID1);
   }
+
   void
   run()
   {
+    FieldVector3D vars;
+
     int tsID = 0;
     while (true)
       {
@@ -189,7 +232,7 @@ public:
             auto &field = vars[tsID][varID][levelID];
             field.init(varList[varID]);
             cdo_read_record(streamID1, field);
-            if (field.nmiss) cdo_abort("Missing value support for operators in module Filter not added yet!");
+            if (field.numMissVals) cdo_abort("Missing value support for operators in module Filter not added yet!");
           }
 
         // get and check time increment
@@ -235,6 +278,11 @@ public:
     auto nts = tsID;
     if (nts <= 1) cdo_abort("Number of time steps <= 1!");
 
+    auto numArrays = vars_numArrays(vars);
+    auto allocatedMem = vars_allocatedMem(vars) * nts;
+    if (Options::cdoVerbose)
+      cdo_print("Allocate %zu array%s over %zu steps: size=%zu Bytes", numArrays, numArrays > 1 ? "s" : "", nts, allocatedMem);
+
     std::vector<FilterMemory> fourierMemory(Threading::ompNumThreads);
 
     if (useFFTW)
@@ -244,6 +292,7 @@ public:
           {
             fm.in_fft = fftw_alloc_complex(nts);
             fm.out_fft = fftw_alloc_complex(nts);
+            std::scoped_lock lock(fftwMutex);
             fm.p_T2S = fftw_plan_dft_1d(nts, fm.in_fft, fm.out_fft, 1, FFTW_ESTIMATE);
             fm.p_S2T = fftw_plan_dft_1d(nts, fm.out_fft, fm.in_fft, -1, FFTW_ESTIMATE);
           }
@@ -294,9 +343,8 @@ public:
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        auto fieldMemType = varList[varID].memType;
-        auto gridsize = varList[varID].gridsize;
-        for (int levelID = 0; levelID < varList[varID].nlevels; ++levelID)
+        const auto &var = varList[varID];
+        for (int levelID = 0; levelID < var.nlevels; ++levelID)
           {
             if (useFFTW)
               {
@@ -304,12 +352,12 @@ public:
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
 #endif
-                for (size_t i = 0; i < gridsize; ++i)
+                for (size_t i = 0; i < var.gridsize; ++i)
                   {
                     auto ompthID = cdo_omp_get_thread_num();
                     auto &fm = fourierMemory[ompthID];
 
-                    if (fieldMemType == MemType::Float)
+                    if (var.memType == MemType::Float)
                       for (int t = 0; t < nts; ++t)
                         {
                           fm.in_fft[t][0] = vars[t][varID][levelID].vec_f[i];
@@ -324,7 +372,7 @@ public:
 
                     filter_fftw(nts, fmasc, fm.out_fft, &fm.p_T2S, &fm.p_S2T);
 
-                    if (fieldMemType == MemType::Float)
+                    if (var.memType == MemType::Float)
                       for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_f[i] = fm.in_fft[t][0] / nts;
                     else
                       for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_d[i] = fm.in_fft[t][0] / nts;
@@ -336,21 +384,21 @@ public:
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
 #endif
-                for (size_t i = 0; i < gridsize; ++i)
+                for (size_t i = 0; i < var.gridsize; ++i)
                   {
                     auto ompthID = cdo_omp_get_thread_num();
                     auto &fm = fourierMemory[ompthID];
 
-                    if (fieldMemType == MemType::Float)
+                    if (var.memType == MemType::Float)
                       for (int t = 0; t < nts; ++t) fm.real[t] = vars[t][varID][levelID].vec_f[i];
                     else
                       for (int t = 0; t < nts; ++t) fm.real[t] = vars[t][varID][levelID].vec_d[i];
 
-                    varray_fill(fm.imag, 0.0);
+                    ranges::fill(fm.imag, 0.0);
 
                     filter_intrinsic(nts, fmasc, fm.real.data(), fm.imag.data());
 
-                    if (fieldMemType == MemType::Float)
+                    if (var.memType == MemType::Float)
                       for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_f[i] = fm.real[t];
                     else
                       for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_d[i] = fm.real[t];
@@ -366,6 +414,7 @@ public:
           {
             fftw_free(fm.in_fft);
             fftw_free(fm.out_fft);
+            std::scoped_lock lock(fftwMutex);
             fftw_destroy_plan(fm.p_T2S);
             fftw_destroy_plan(fm.p_S2T);
           }
@@ -383,7 +432,8 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            for (int levelID = 0; levelID < varList[varID].nlevels; ++levelID)
+            const auto &var = varList[varID];
+            for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
                 auto &field = vars[tsID][varID][levelID];
                 if (field.hasData())
@@ -401,17 +451,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Filter(void *process)
-{
-  ModuleFilter filter;
-  filter.init(process);
-  filter.run();
-  filter.close();
-  return nullptr;
-}
diff --git a/src/Fldrms.cc b/src/Fldrms.cc
index 679fcd0e2a55109416fa5cc1c7ddae3d2d4e0ab4..1a55061754705827b6b8751347318921f426d598 100644
--- a/src/Fldrms.cc
+++ b/src/Fldrms.cc
@@ -16,8 +16,19 @@
 #include <mpim_grid.h>
 #include "field_functions.h"
 
-class ModuleFldrms
+class Fldrms : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Fldrms",
+    .operators = { { "fldrms"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Fldrms> registration = RegisterEntry<Fldrms>(module);
   int lastgrid = -1;
 
   CdoStreamID streamID1;
@@ -34,9 +45,8 @@ class ModuleFldrms
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -113,9 +123,9 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, field1.vec_d.data(), &field1.nmiss);
+            cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
             cdo_inq_record(streamID2, &varID, &levelID);
-            cdo_read_record(streamID2, field2.vec_d.data(), &field2.nmiss);
+            cdo_read_record(streamID2, field2.vec_d.data(), &field2.numMissVals);
 
             field1.grid = varList1[varID].gridID;
             field2.grid = varList2[varID].gridID;
@@ -140,7 +150,7 @@ public:
             field_rms(field1, field2, field3);
 
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, field3.vec_d.data(), field3.nmiss);
+            cdo_write_record(streamID3, field3.vec_d.data(), field3.numMissVals);
           }
 
         tsID++;
@@ -152,17 +162,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Fldrms(void *process)
-{
-  ModuleFldrms fldrms;
-  fldrms.init(process);
-  fldrms.run();
-  fldrms.close();
-  return nullptr;
-}
diff --git a/src/Fldstat.cc b/src/Fldstat.cc
index 3268b2a96b75979d0a9ae33161edaacf8cc25060..c58068d282bbeb4b411bbca4d8c333fe92bf7d49 100644
--- a/src/Fldstat.cc
+++ b/src/Fldstat.cc
@@ -37,7 +37,7 @@ void gridcell_areas(int gridID, Varray<double> &array);
 
 template <typename T>
 static void
-print_location_LL(int operfunc, const CdoVar &var, int levelID, double sglval, const Varray<T> &fieldvec, CdiDateTime vDateTime)
+print_location_LL(int operfunc, const CdoVar &var, int levelID, double sglval, const Varray<T> &v, CdiDateTime vDateTime)
 {
   static auto printHeader = true;
   const char *funcName = (operfunc == FieldFunc_Min) ? "Minval" : "Maxval";
@@ -51,7 +51,7 @@ print_location_LL(int operfunc, const CdoVar &var, int levelID, double sglval, c
       T value = sglval;
       for (size_t ij = 0; ij < var.gridsize; ++ij)
         {
-          if (dbl_is_equal(fieldvec[ij], value))
+          if (dbl_is_equal(v[ij], value))
             {
               auto j = ij / nlon;
               auto i = ij - j * nlon;
@@ -71,18 +71,31 @@ print_location_LL(int operfunc, const CdoVar &var, int levelID, double sglval, c
     }
 }
 
+static void
+print_location_LL(int operfunc, const CdoVar &var, int levelID, double sglval, const Field &field, CdiDateTime vDateTime)
+{
+  if (field.memType == MemType::Float)
+    print_location_LL(operfunc, var, levelID, sglval, field.vec_f, vDateTime);
+  else
+    print_location_LL(operfunc, var, levelID, sglval, field.vec_d, vDateTime);
+}
+
 template <typename T>
 static void
-field_mul_weights(size_t len, Varray<T> &v1, const Varray<double> &v2, size_t nmiss, T missval)
+field_mul_weights(Varray<T> &v1, const Varray<double> &v2, size_t numMissVals, T missval)
 {
-  if (nmiss)
+  assert(v1.size() > 0);
+  assert(v2.size() == v1.size());
+
+  auto gridSize = v1.size();
+  if (numMissVals)
     {
-      for (size_t i = 0; i < len; ++i)
+      for (size_t i = 0; i < gridSize; ++i)
         if (!dbl_is_equal(v1[i], missval)) v1[i] *= v2[i];
     }
   else
     {
-      for (size_t i = 0; i < len; ++i) v1[i] *= v2[i];
+      for (size_t i = 0; i < gridSize; ++i) v1[i] *= v2[i];
     }
 }
 
@@ -90,9 +103,9 @@ static void
 field_mul_weights(Field &field)
 {
   if (field.memType == MemType::Float)
-    field_mul_weights(field.size, field.vec_f, field.weightv, field.nmiss, (float) field.missval);
+    field_mul_weights(field.vec_f, field.weightv, field.numMissVals, (float) field.missval);
   else
-    field_mul_weights(field.size, field.vec_d, field.weightv, field.nmiss, field.missval);
+    field_mul_weights(field.vec_d, field.weightv, field.numMissVals, field.missval);
 }
 
 static void
@@ -154,7 +167,7 @@ fldstatGetParameter(bool &useWeights)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -172,8 +185,68 @@ fldstatGetParameter(bool &useWeights)
     }
 }
 
-class ModuleFldstat
+static int
+get_gridcell_weights(Field &field, bool useWeights, bool doPrintWarning, int ngrids, std::string &varName)
+{
+  auto gridSize = field.size;
+  field.weightv.resize(gridSize);
+  if (!useWeights)
+    {
+      cdo_print("Using constant grid cell area weights!");
+      ranges::fill(field.weightv, 1.0);
+    }
+
+  field.weightv[0] = 1;
+  if (useWeights && field.size > 1)
+    {
+      auto wstatus = (gridcell_weights(field.grid, field.weightv) != 0);
+      if (wstatus && doPrintWarning) printWeightsWarning(ngrids, varName);
+    }
+
+  return field.grid;
+}
+
+static int
+get_gridcell_areas(Field &field)
+{
+  auto gridSize = field.size;
+  field.weightv.resize(gridSize);
+
+  gridcell_areas(field.grid, field.weightv);
+
+  return field.grid;
+}
+
+class Fldstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Fldstat",
+    .operators = { { "fldrange", FieldFunc_Range, 0, FldstatHelp },
+                   { "fldmin", FieldFunc_Min, 0, FldstatHelp },
+                   { "fldmax", FieldFunc_Max, 0, FldstatHelp },
+                   { "fldsum", FieldFunc_Sum, 0, FldstatHelp },
+                   { "fldint", FieldFunc_Sum, 0, FldstatHelp },
+                   { "fldmean", FieldFunc_Meanw, 1, FldstatHelp },
+                   { "fldavg", FieldFunc_Avgw, 1, FldstatHelp },
+                   { "fldstd", FieldFunc_Stdw, 1, FldstatHelp },
+                   { "fldstd1", FieldFunc_Std1w, 1, FldstatHelp },
+                   { "fldvar", FieldFunc_Varw, 1, FldstatHelp },
+                   { "fldvar1", FieldFunc_Var1w, 1, FldstatHelp },
+                   { "fldskew", FieldFunc_Skew, 0, FldstatHelp },
+                   { "fldkurt", FieldFunc_Kurt, 0, FldstatHelp },
+                   { "fldmedian", FieldFunc_Median, 0, FldstatHelp },
+                   { "fldcount", FieldFunc_Count, 0, FldstatHelp },
+                   { "fldpctl", FieldFunc_Pctl, 0, FldstatHelp } },
+    .aliases = { { "globavg", "fldavg" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Fldstat> registration = RegisterEntry<Fldstat>(module);
+
+  int FLDINT;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -198,28 +271,9 @@ class ModuleFldstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-                cdo_operator_add("fldrange",  FieldFunc_Range,  0, nullptr);
-                cdo_operator_add("fldmin",    FieldFunc_Min,    0, nullptr);
-                cdo_operator_add("fldmax",    FieldFunc_Max,    0, nullptr);
-                cdo_operator_add("fldsum",    FieldFunc_Sum,    0, nullptr);
-  auto FLDINT = cdo_operator_add("fldint",    FieldFunc_Sum,    0, nullptr);
-                cdo_operator_add("fldmean",   FieldFunc_Meanw,  1, nullptr);
-                cdo_operator_add("fldavg",    FieldFunc_Avgw,   1, nullptr);
-                cdo_operator_add("fldstd",    FieldFunc_Stdw,   1, nullptr);
-                cdo_operator_add("fldstd1",   FieldFunc_Std1w,  1, nullptr);
-                cdo_operator_add("fldvar",    FieldFunc_Varw,   1, nullptr);
-                cdo_operator_add("fldvar1",   FieldFunc_Var1w,  1, nullptr);
-                cdo_operator_add("fldskew",   FieldFunc_Skew,   0, nullptr);
-                cdo_operator_add("fldkurt",   FieldFunc_Kurt,   0, nullptr);
-                cdo_operator_add("fldmedian", FieldFunc_Median, 0, nullptr);
-                cdo_operator_add("fldcount",  FieldFunc_Count,  0, nullptr);
-                cdo_operator_add("fldpctl",   FieldFunc_Pctl,   0, nullptr);
-    // clang-format on
+    FLDINT = module.get_id("fldint");
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -234,14 +288,8 @@ public:
         operator_input_arg("percentile number");
         pn = parameter_to_double(cdo_operator_argv(0));
       }
-    else if (needWeights)
-      {
-        fldstatGetParameter(useWeights);
-      }
-    else
-      {
-        operator_check_argc(0);
-      }
+    else if (needWeights) { fldstatGetParameter(useWeights); }
+    else { operator_check_argc(0); }
 
     streamID1 = cdo_open_read(0);
 
@@ -268,17 +316,6 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    auto gridsizemax = vlistGridsizeMax(vlistID1);
-
-    if (needWeights || needCellarea)
-      {
-        field.weightv.resize(gridsizemax);
-        if (needWeights && !useWeights)
-          {
-            cdo_print("Using constant grid cell area weights!");
-            for (size_t i = 0; i < gridsizemax; ++i) field.weightv[i] = 1.0;
-          }
-      }
     varList_init(varList1, vlistID1);
 
     ntsteps = vlistNtsteps(vlistID1);
@@ -303,7 +340,7 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            double fstatus = (ntsteps > 1) ? (tsID + (recID + 1.0) / nrecs) / ntsteps : 1.0;
+            double fstatus = (ntsteps > 1) ? ((tsID + (recID + 1.0) / nrecs) / ntsteps) : 1.0;
             if (!Options::cdoVerbose && tsID > 0) progress::update(0, 1, fstatus);
 
             int varID, levelID;
@@ -312,39 +349,20 @@ public:
             field.init(var);
             cdo_read_record(streamID1, field);
 
-            if (needWeights && field.grid != lastgrid)
-              {
-                lastgrid = field.grid;
-                field.weightv[0] = 1;
-                if (useWeights && field.size > 1)
-                  {
-                    auto wstatus = (gridcell_weights(field.grid, field.weightv) != 0);
-                    if (wstatus && tsID == 0 && levelID == 0) printWeightsWarning(ngrids, var.name);
-                  }
-              }
-
-            if (needCellarea && field.grid != lastgrid)
-              {
-                lastgrid = field.grid;
-                gridcell_areas(field.grid, field.weightv);
-              }
+            auto doPrintWarning = (tsID == 0 && levelID == 0);
+            if (needWeights && field.grid != lastgrid) lastgrid = get_gridcell_weights(field, useWeights, doPrintWarning, ngrids, var.name);
+            else if (needCellarea && field.grid != lastgrid) lastgrid = get_gridcell_areas(field);
 
             if (needCellarea) field_mul_weights(field);
 
             auto singleValue = (operfunc == FieldFunc_Pctl) ? field_pctl(field, pn) : field_function(field, operfunc);
 
-            if (Options::cdoVerbose && isMinMaxFunc)
-              {
-                if (field.memType == MemType::Float)
-                  print_location_LL(operfunc, var, levelID, singleValue, field.vec_f, vDateTime);
-                else
-                  print_location_LL(operfunc, var, levelID, singleValue, field.vec_d, vDateTime);
-              }
+            if (Options::cdoVerbose && isMinMaxFunc) { print_location_LL(operfunc, var, levelID, singleValue, field, vDateTime); }
 
-            size_t nmiss = dbl_is_equal(singleValue, field.missval);
+            size_t numMissVals = dbl_is_equal(singleValue, field.missval);
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, &singleValue, nmiss);
+            cdo_write_record(streamID2, &singleValue, numMissVals);
           }
 
         tsID++;
@@ -360,18 +378,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Fldstat(void *process)
-{
-  ModuleFldstat fldstat;
-  fldstat.init(process);
-  fldstat.run();
-  fldstat.close();
-
-  return nullptr;
-}
diff --git a/src/Fldstat2.cc b/src/Fldstat2.cc
index f5f62b8115f9825738ed85d7ed9a3179382bf1f8..bb643b758dea4ac811e89b47fafa07b6e2797f5a 100644
--- a/src/Fldstat2.cc
+++ b/src/Fldstat2.cc
@@ -122,8 +122,19 @@ covariance(const Field &field1, const Field &field2, const Varray<double> &weigh
     return covariance(field1.vec_d, field2.vec_d, weight, field1.missval, field2.missval, field1.size);
 }
 
-class ModuleFldstat2
+class Fldstat2 : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Fldstat2",
+    .operators = { { "fldcor", FieldFunc_Cor, 0, FldcorHelp }, { "fldcovar", FieldFunc_Covar, 0, FldcovarHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Fldstat2> registration = RegisterEntry<Fldstat2>(module);
   int operfunc;
 
   CdoStreamID streamID1;
@@ -143,13 +154,10 @@ class ModuleFldstat2
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    cdo_operator_add("fldcor",   FieldFunc_Cor,   0, nullptr);
-    cdo_operator_add("fldcovar", FieldFunc_Covar, 0, nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -234,10 +242,10 @@ public:
             else if (operfunc == FieldFunc_Covar)
               sglval = covariance(field1, field2, weight);
 
-            auto nmiss3 = DBL_IS_EQUAL(sglval, varList1[varID].missval) ? 1 : 0;
+            auto numMissVals3 = DBL_IS_EQUAL(sglval, varList1[varID].missval) ? 1 : 0;
 
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, &sglval, nmiss3);
+            cdo_write_record(streamID3, &sglval, numMissVals3);
           }
 
         tsID++;
@@ -250,18 +258,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Fldstat2(void *process)
-{
-  ModuleFldstat2 fldstat2;
-  fldstat2.init(process);
-  fldstat2.run();
-  fldstat2.close();
-
-  return nullptr;
-}
diff --git a/src/Fourier.cc b/src/Fourier.cc
index a7a056be3210500dd77608ee40322359d57cf79d..be138003cda37a853633fe6a922c0a60b740e20d 100644
--- a/src/Fourier.cc
+++ b/src/Fourier.cc
@@ -11,6 +11,8 @@
 
 #ifdef HAVE_LIBFFTW3
 #include <fftw3.h>
+#include <mutex>
+static std::mutex fftwMutex;
 #endif
 
 #include <cdi.h>
@@ -132,8 +134,20 @@ fourier_intrinsic(int sign, int varID, int levelID, int nts, size_t gridsize, do
     }
 }
 
-class ModuleFourier
+class Fourier : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Fourier",
+    .operators = { { "fourier", FourierHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_COMP,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Fourier> registration = RegisterEntry<Fourier>(module);
+
   size_t nalloc = 0;
 
   CdoStreamID streamID1;
@@ -154,10 +168,8 @@ class ModuleFourier
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     if (Options::Use_FFTW)
       {
 #ifdef HAVE_LIBFFTW3
@@ -189,6 +201,7 @@ public:
 
     nvars = vlistNvars(vlistID1);
   }
+
   void
   run()
   {
@@ -216,9 +229,9 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             auto gridsize = varList[varID].gridsize;
             vars[tsID][varID][levelID].resize(2 * gridsize);
-            size_t nmiss;
-            cdo_read_record(streamID1, vars[tsID][varID][levelID].vec_d.data(), &nmiss);
-            vars[tsID][varID][levelID].nmiss = nmiss;
+            size_t numMissVals;
+            cdo_read_record(streamID1, vars[tsID][varID][levelID].vec_d.data(), &numMissVals);
+            vars[tsID][varID][levelID].numMissVals = numMissVals;
           }
 
         tsID++;
@@ -235,6 +248,7 @@ public:
           {
             fm.in_fft = fftw_alloc_complex(nts);
             fm.out_fft = fftw_alloc_complex(nts);
+            std::scoped_lock lock(fftwMutex);
             fm.plan = fftw_plan_dft_1d(nts, fm.in_fft, fm.out_fft, sign, FFTW_ESTIMATE);
           }
         if (Options::cdoVerbose) fftw_print_plan(fourierMemory[0].plan);
@@ -273,6 +287,7 @@ public:
           {
             fftw_free(fm.in_fft);
             fftw_free(fm.out_fft);
+            std::scoped_lock lock(fftwMutex);
             fftw_destroy_plan(fm.plan);
           }
         fftw_cleanup();
@@ -291,32 +306,19 @@ public:
               {
                 if (!vars[tsID][varID][levelID].empty())
                   {
-                    auto nmiss = vars[tsID][varID][levelID].nmiss;
+                    auto numMissVals = vars[tsID][varID][levelID].numMissVals;
                     cdo_def_record(streamID2, varID, levelID);
-                    cdo_write_record(streamID2, vars[tsID][varID][levelID].vec_d.data(), nmiss);
+                    cdo_write_record(streamID2, vars[tsID][varID][levelID].vec_d.data(), numMissVals);
                   }
               }
           }
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Fourier(void *process)
-{
-  ModuleFourier furier;
-
-  furier.init(process);
-  furier.run();
-  furier.close();
-
-  return nullptr;
-}
diff --git a/src/Gengrid.cc b/src/Gengrid.cc
index 2556005d561ccbcb6f4bfece101b727f60c5f27f..a71350c173f216b40cb2dc504ab53e92b4a8ecd8 100644
--- a/src/Gengrid.cc
+++ b/src/Gengrid.cc
@@ -16,10 +16,21 @@
 #include "process_int.h"
 #include "cdo_zaxis.h"
 
-class ModuleGengrid
+class Gengrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Gengrid",
+    .operators = { { "gengrid"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Gengrid> registration = RegisterEntry<Gengrid>(module);
   int varID, levelID;
-  size_t nmiss1, nmiss2;
+  size_t numMissVals1, numMissVals2;
   double missval = 0;
 
   CdoStreamID streamID3;
@@ -29,9 +40,8 @@ class ModuleGengrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -57,16 +67,16 @@ public:
     cdo_stream_inq_timestep(streamID2, 0);
 
     cdo_inq_record(streamID1, &varID, &levelID);
-    cdo_read_record(streamID1, array1.data(), &nmiss1);
+    cdo_read_record(streamID1, array1.data(), &numMissVals1);
     cdo_inq_record(streamID2, &varID, &levelID);
-    cdo_read_record(streamID2, array2.data(), &nmiss2);
+    cdo_read_record(streamID2, array2.data(), &numMissVals2);
 
     auto datatype = vlistInqVarDatatype(vlistID1, 0);
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
 
-    if (nmiss1 || nmiss2) cdo_abort("Missing values unsupported!");
+    if (numMissVals1 || numMissVals2) cdo_abort("Missing values unsupported!");
 
     auto gridID3 = gridCreate(GRID_CURVILINEAR, gridsize);
 
@@ -128,19 +138,5 @@ public:
   close()
   {
     cdo_stream_close(streamID3);
-
-    cdo_finish();
   }
 };
-
-void *
-Gengrid(void *process)
-{
-  ModuleGengrid gengrid;
-
-  gengrid.init(process);
-  gengrid.run();
-  gengrid.close();
-
-  return nullptr;
-}
diff --git a/src/Getgridcell.cc b/src/Getgridcell.cc
index 00bbf7742551b45712ab2ed985f13501ad459f50..bc5f591a7db9b573588461822b6aefcbca6ec928 100644
--- a/src/Getgridcell.cc
+++ b/src/Getgridcell.cc
@@ -81,7 +81,7 @@ get_parameter()
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -118,20 +118,28 @@ check_radius_range(double radius, const char *name)
   if (radius < 0.0 || radius > 180.0) cdo_abort("%s=%g out of bounds (0-180 deg)!", name, radius);
 }
 
-class ModuleGetgridcell
+class Getgridcell : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Getgridcell",
+    .operators = { { "gridcellindex", 0, 0, "lon/lat coordinate of a single cell", GetgridcellHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Getgridcell> registration = RegisterEntry<Getgridcell>(module);
+
   CdoStreamID streamID1;
   GridPoint gridPoint;
   int gridID1;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("gridcellindex", 0, 0, "lon/lat coordinate of a single cell");
-
     gridPoint = get_parameter();
 
     check_radius_range(gridPoint.radius, "radius");
@@ -167,18 +175,5 @@ public:
   close()
   {
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Getgridcell(void *process)
-{
-  ModuleGetgridcell getgridcell;
-  getgridcell.init(process);
-  getgridcell.run();
-  getgridcell.close();
-
-  return nullptr;
-}
diff --git a/src/Gradsdes.cc b/src/Gradsdes.cc
index d5ea7dff1d78ce302f5589c23c3ec18244a21c7c..a0836343dc211526be4afc301db1a2ad366be20c 100644
--- a/src/Gradsdes.cc
+++ b/src/Gradsdes.cc
@@ -213,7 +213,7 @@ flt2ibm(float x, unsigned char *ibm)
 #define Put4Byte(buf, cnt, ival) \
   ((buf[cnt++] = (ival) >> 24), (buf[cnt++] = (ival) >> 16), (buf[cnt++] = (ival) >> 8), (buf[cnt++] = (ival)))
 
-#define PutInt(buf, cnt, ival) ((ival < 0) ? Put4Byte(buf, cnt, 0x7fffffff - ival + 1) : Put4Byte(buf, cnt, ival))
+#define PutInt(buf, cnt, ival) ((ival < 0) ? Put4Byte(buf, cnt, 0x7fffffffU - ival + 1) : Put4Byte(buf, cnt, ival))
 
 static void
 dumpmap()
@@ -222,7 +222,6 @@ dumpmap()
   unsigned char vermap;
   unsigned char mrec[512];
   int swpflg = 0;
-  int i;
   int nrecords = 0;
   struct gaindx indx;
   struct gaindxb indxb;
@@ -268,7 +267,7 @@ dumpmap()
       if (indx.hinum > 0)
         {
           indx.hipnt = new int[indx.hinum];
-          for (i = 0; i < indx.hinum; ++i)
+          for (int i = 0; i < indx.hinum; ++i)
             {
               nbytes = fread(mrec, sizeof(unsigned char), 4, mapfp);
               indx.hipnt[i] = GET_UINT4(mrec[0], mrec[1], mrec[2], mrec[3]);
@@ -281,18 +280,18 @@ dumpmap()
         }
       if (indx.intnum > 0)
         {
-          indx.intpnt = new int [indx.intnum];
-          for (i = 0; i < indx.intnum; ++i)
+          indx.intpnt = new int[indx.intnum];
+          for (int i = 0; i < indx.intnum; ++i)
             {
               nbytes = fread(mrec, sizeof(unsigned char), 4, mapfp);
               indx.intpnt[i] = GET_UINT4(mrec[0], mrec[1], mrec[2], mrec[3]);
-              if (indx.intpnt[i] < 0) indx.intpnt[i] = 0x7fffffff - indx.intpnt[i] + 1;
+              if (indx.intpnt[i] < 0) indx.intpnt[i] = 0x7fffffffU - indx.intpnt[i] + 1;
             }
         }
       if (indx.fltnum > 0)
         {
           indx.fltpnt = new float[indx.fltnum];
-          for (i = 0; i < indx.fltnum; ++i)
+          for (int i = 0; i < indx.fltnum; ++i)
             {
               nbytes = fread(urec, sizeof(unsigned char), 4, mapfp);
               indx.fltpnt[i] = ibm2flt(urec);
@@ -349,11 +348,11 @@ dumpmap()
   std::fclose(mapfp);
 
   printf("hinum: %d\n", indx.hinum);
-  for (i = 0; i < indx.hinum; ++i) printf("%3d %5d\n", i + 1, indx.hipnt[i]);
+  for (int i = 0; i < indx.hinum; ++i) printf("%3d %5d\n", i + 1, indx.hipnt[i]);
 
   printf("\n");
   printf("hfnum: %d\n", indx.hfnum);
-  for (i = 0; i < indx.hfnum; ++i) printf("%3d %g\n", i + 1, indx.hfpnt[i]);
+  for (int i = 0; i < indx.hfnum; ++i) printf("%3d %g\n", i + 1, indx.hfpnt[i]);
 
   printf("\n");
 
@@ -362,36 +361,36 @@ dumpmap()
   if (indx.intnum == indx.fltnum)
     {
       printf("num: %d\n", indx.intnum);
-      for (i = 0; i < indx.intnum / 3; ++i)
+      for (int i = 0; i < indx.intnum / 3; ++i)
         printf("%3d %8d %6d %4d %8g %10g %8g\n", i + 1, indx.intpnt[i * 3], indx.intpnt[i * 3 + 1], indx.intpnt[i * 3 + 2],
                indx.fltpnt[i * 3], indx.fltpnt[i * 3 + 1], indx.fltpnt[i * 3 + 2]);
     }
   else if (indx.intnum == nrecords && indx.fltnum == nrecords * 3 && indxb.bignum == nrecords * 2)
     {
       printf("nrecords: %d\n", nrecords);
-      for (i = 0; i < nrecords; ++i)
+      for (int i = 0; i < nrecords; ++i)
         printf("%3d %8zd %6zd %4d %8g %10g %8g\n", i + 1, (size_t) indxb.bigpnt[i * 2], (size_t) indxb.bigpnt[i * 2 + 1],
                indx.intpnt[i], indx.fltpnt[i * 3], indx.fltpnt[i * 3 + 1], indx.fltpnt[i * 3 + 2]);
     }
   else
     {
       printf("intnum: %d\n", indx.intnum);
-      for (i = 0; i < indx.intnum; ++i) printf("%3d %d\n", i + 1, indx.intpnt[i]);
+      for (int i = 0; i < indx.intnum; ++i) printf("%3d %d\n", i + 1, indx.intpnt[i]);
 
       printf("\n");
       printf("fltnum: %d\n", indx.fltnum);
-      for (i = 0; i < indx.fltnum; ++i) printf("%3d %g\n", i + 1, indx.fltpnt[i]);
+      for (int i = 0; i < indx.fltnum; ++i) printf("%3d %g\n", i + 1, indx.fltpnt[i]);
 
       printf("\n");
       printf("bignum: %d\n", indxb.bignum);
-      for (i = 0; i < indxb.bignum; ++i) printf("%3d %zd\n", i + 1, (size_t) indxb.bigpnt[i]);
+      for (int i = 0; i < indxb.bignum; ++i) printf("%3d %zd\n", i + 1, (size_t) indxb.bigpnt[i]);
     }
 }
 
 static void
 ctl_xydef(FILE *gdp, int gridID, bool *yrev)
 {
-  int i, j;
+  int j;
 
   *yrev = false;
 
@@ -423,7 +422,7 @@ ctl_xydef(FILE *gdp, int gridID, bool *yrev)
       Varray<double> xvals(xsize * ysize), yvals(xsize * ysize);
       gridInqXvals(gridID, xvals.data());
       gridInqYvals(gridID, yvals.data());
-      for (i = 0; i < xsize * ysize; ++i)
+      for (int i = 0; i < xsize * ysize; ++i)
         {
           if (xvals[i] > 180) xvals[i] -= 360;
           if (xvals[i] < xmin) xmin = xvals[i];
@@ -438,7 +437,7 @@ ctl_xydef(FILE *gdp, int gridID, bool *yrev)
       yrange = ((int) (ymax + 1.5)) - yfirst;
 
       ni = sizeof(inc) / sizeof(inc[0]);
-      for (i = 0; i < ni; ++i)
+      for (int i = 0; i < ni; ++i)
         {
           xinc = yinc = inc[i];
           nx = 1 + (int) (xrange / xinc);
@@ -463,7 +462,7 @@ ctl_xydef(FILE *gdp, int gridID, bool *yrev)
           gridInqXvals(gridID, xvals.data());
           fprintf(gdp, "XDEF %d LEVELS ", xsize);
           j = 0;
-          for (i = 0; i < xsize; ++i)
+          for (int i = 0; i < xsize; ++i)
             {
               fprintf(gdp, "%7.3f ", xvals[i]);
               j++;
@@ -500,7 +499,7 @@ ctl_xydef(FILE *gdp, int gridID, bool *yrev)
           if (yvals[0] > yvals[ysize - 1])
             {
               *yrev = true;
-              for (i = ysize - 1; i >= 0; i--)
+              for (int i = ysize - 1; i >= 0; i--)
                 {
                   fprintf(gdp, "%7.3f ", yvals[i]);
                   j++;
@@ -514,7 +513,7 @@ ctl_xydef(FILE *gdp, int gridID, bool *yrev)
             }
           else
             {
-              for (i = 0; i < ysize; ++i)
+              for (int i = 0; i < ysize; ++i)
                 {
                   fprintf(gdp, "%7.3f ", yvals[i]);
                   j++;
@@ -546,7 +545,7 @@ ctl_xydef(FILE *gdp, int gridID, bool *yrev)
 static void
 ctl_zdef(FILE *gdp, int vlistID, bool *zrev)
 {
-  int i, j, index;
+  int j;
   int zaxisIDmax = -1;
   int zaxisID, nlev;
   double levinc = 0;
@@ -555,7 +554,7 @@ ctl_zdef(FILE *gdp, int vlistID, bool *zrev)
   int nzaxis = vlistNzaxis(vlistID);
 
   int nlevmax = 0;
-  for (index = 0; index < nzaxis; ++index)
+  for (int index = 0; index < nzaxis; ++index)
     {
       zaxisID = vlistZaxis(vlistID, index);
       nlev = zaxisInqSize(zaxisID);
@@ -578,7 +577,7 @@ ctl_zdef(FILE *gdp, int vlistID, bool *zrev)
 
       if (IS_EQUAL(levinc, 1)) *zrev = false;
 
-      for (i = 1; i < nlevmax; ++i)
+      for (int i = 1; i < nlevmax; ++i)
         {
           if (is_not_equal(levinc, (levels[i] - levels[i - 1])))
             {
@@ -597,7 +596,7 @@ ctl_zdef(FILE *gdp, int vlistID, bool *zrev)
       /* zrev not needed !!!
       if ( *zrev )
         {
-          for ( i = nlevmax-1; i >=0 ; i-- )
+          for (i = nlevmax-1; i >= 0; i--)
             {
               if ( lplev ) fprintf(gdp, "%g ", levels[i]/100);
               else         fprintf(gdp, "%d ", (int) levels[i]);
@@ -613,7 +612,7 @@ ctl_zdef(FILE *gdp, int vlistID, bool *zrev)
       else
       */
       {
-        for (i = 0; i < nlevmax; ++i)
+        for (int i = 0; i < nlevmax; ++i)
           {
             if (lplev)
               fprintf(gdp, "%g ", levels[i] / 100);
@@ -754,10 +753,8 @@ ctl_vars(FILE *gdp, int filetype, int vlistID, const VarList &varList, int nvars
 static void
 write_map_grib1(const char *ctlfile, int map_version, int nrecords, int *intnum, float *fltnum, off_t *bignum)
 {
-  int i;
   struct gaindx indx;
   struct gaindxb indxb;
-  int hinum[5];
 
   memset(&indx, 0, sizeof(struct gaindx));
 
@@ -786,6 +783,7 @@ write_map_grib1(const char *ctlfile, int map_version, int nrecords, int *intnum,
   indx.fltpnt = nullptr;
   indxb.bigpnt = nullptr;
 
+  int hinum[5];
   hinum[0] = map_version;
   hinum[1] = 1;
   hinum[2] = nrecords;
@@ -794,7 +792,7 @@ write_map_grib1(const char *ctlfile, int map_version, int nrecords, int *intnum,
 
   if (map_version == 2)
     {
-      int nb, bcnt, rc, j;
+      int nb, bcnt, rc;
       float fdum;
       unsigned char ibmfloat[4];
 
@@ -827,35 +825,35 @@ write_map_grib1(const char *ctlfile, int map_version, int nrecords, int *intnum,
       Put1Byte(map, bcnt, 0); /* initial second */
 
       if (indx.hinum)
-        for (i = 0; i < indx.hinum; ++i) Put4Byte(map, bcnt, hinum[i]);
+        for (int i = 0; i < indx.hinum; ++i) Put4Byte(map, bcnt, hinum[i]);
 
       if (indx.hfnum)
         { /* blank for now */
         }
 
-      for (i = 0; i < nrecords; ++i)
+      for (int i = 0; i < nrecords; ++i)
         {
           PutInt(map, bcnt, (int) bignum[i * 2]);
           PutInt(map, bcnt, (int) bignum[i * 2 + 1]);
           PutInt(map, bcnt, intnum[i]);
         }
 
-      for (i = 0; i < indx.fltnum; ++i)
+      for (int i = 0; i < indx.fltnum; ++i)
         {
           fdum = fltnum[i];
           rc = flt2ibm(fdum, ibmfloat);
           if (rc < 0) cdo_abort("overflow in IBM float conversion");
-          for (j = 0; j < 4; ++j) map[bcnt++] = ibmfloat[j];
+          for (int j = 0; j < 4; ++j) map[bcnt++] = ibmfloat[j];
         }
 
       /* write out the factors for converting from grid to absolute time */
 
-      for (i = 0; i < 8; ++i)
+      for (int i = 0; i < 8; ++i)
         {
           fdum = 0;
           rc = flt2ibm(fdum, ibmfloat);
           if (rc < 0) cdo_abort("overflow in IBM float conversion");
-          for (j = 0; j < 4; ++j) map[bcnt++] = ibmfloat[j];
+          for (int j = 0; j < 4; ++j) map[bcnt++] = ibmfloat[j];
         }
 
       fwrite(map, 1, bcnt, mapfp);
@@ -869,7 +867,7 @@ write_map_grib1(const char *ctlfile, int map_version, int nrecords, int *intnum,
       if (map_version == 1)
         {
           std::vector<int> intnumbuf(indx.intnum);
-          for (i = 0; i < nrecords; ++i)
+          for (int i = 0; i < nrecords; ++i)
             {
               intnumbuf[i * 3 + 0] = (int) bignum[i * 2];
               intnumbuf[i * 3 + 1] = (int) bignum[i * 2 + 1];
@@ -897,8 +895,21 @@ void write_map_grib2(const char *ctlfile, int map_version, int nrecords, int
 }
 */
 
-class ModuleGradsdes
+class Gradsdes : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Gradsdes",
+    .operators = { { "gradsdes", GradsdesHelp }, { "dumpmap", GradsdesHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, FilesOnly },
+  };
+  inline static RegisterEntry<Gradsdes> registration = RegisterEntry<Gradsdes>(module);
+
+  int GRADSDES, DUMPMAP;
   int gridID = -1;
   int gridtype = -1;
   int index;
@@ -948,8 +959,6 @@ class ModuleGradsdes
   char *ctlfile;
   const char *datfile;
 
-  int GRADSDES, DUMPMAP;
-
   void
   _run(int &tsID)
   {
@@ -1059,8 +1068,8 @@ class ModuleGradsdes
                 cdo_inq_record(streamID, &varID, &levelID);
                 if (vars[varID] == true)
                   {
-                    size_t nmiss;
-                    cdo_read_record(streamID, array.data(), &nmiss);
+                    size_t numMissVals;
+                    cdo_read_record(streamID, array.data(), &numMissVals);
 
                     index = (tsID * nrecsout + recoffset[varID] + levelID);
 
@@ -1088,14 +1097,10 @@ class ModuleGradsdes
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    GRADSDES = cdo_operator_add("gradsdes",  0, 0, nullptr);
-    DUMPMAP  = cdo_operator_add("dumpmap",   0, 0, nullptr);
-    // clang-format on
+    GRADSDES = module.get_id("gradsdes");
+    DUMPMAP = module.get_id("dumpmap");
 
     (void) (GRADSDES);  // unused
 
@@ -1305,6 +1310,8 @@ public:
   void
   run()
   {
+    if (operatorID == DUMPMAP) return;
+
     int tsID = 0;
 
     _run(tsID);
@@ -1339,8 +1346,7 @@ public:
     int xsize = gridInqXsize(gridID);
     int ysize = gridInqYsize(gridID);
 
-    int res = 0;
-    if (gridtype == GRID_GAUSSIAN) res = nlat_to_ntr(ysize);
+    int res = (gridtype == GRID_GAUSSIAN) ? nlat_to_ntr(ysize) : 0;
 
     if (res)
       fprintf(gdp, "TITLE  %s  T%d grid\n", datfile, res);
@@ -1373,25 +1379,11 @@ public:
   void
   close()
   {
-    if (operatorID != DUMPMAP)
-      {
-        cdo_stream_close(streamID);
+    if (operatorID == DUMPMAP) return;
 
-        if (ctlfile) delete[] ctlfile;
-        if (idxfile) delete[] idxfile;
-      }
+    cdo_stream_close(streamID);
 
-    cdo_finish();
+    if (ctlfile) delete[] ctlfile;
+    if (idxfile) delete[] idxfile;
   }
 };
-
-void *
-Gradsdes(void *process)
-{
-  ModuleGradsdes gradsdes;
-  gradsdes.init(process);
-  gradsdes.run();
-  gradsdes.close();
-
-  return nullptr;
-}
diff --git a/src/Gridboxstat.cc b/src/Gridboxstat.cc
index dd3ccb050deb08f2a573c2cb9bc612c783d7b956..163c26bdc039c78be98d278530e9624fcd8cd036 100644
--- a/src/Gridboxstat.cc
+++ b/src/Gridboxstat.cc
@@ -216,23 +216,12 @@ gen_boxgrid(int gridID1, size_t xinc, size_t yinc)
       gridDefXsize(gridID2, nlon2);
       gridDefYsize(gridID2, nlat2);
     }
-  else
-    {
-      cdo_abort("Unsupported grid: %s", gridNamePtr(gridtype));
-    }
+  else { cdo_abort("Unsupported grid: %s", gridNamePtr(gridtype)); }
 
   if (gridtype == GRID_GAUSSIAN || gridtype == GRID_LONLAT) { gen_boxgrid_reg2D(gridID1, xinc, yinc, gridID2); }
-  else if (gridtype == GRID_GENERIC)
-    {
-    }
-  else if (gridtype == GRID_CURVILINEAR)
-    {
-      gen_boxgrid_curv2D(gridID1, xinc, yinc, gridID2);
-    }
-  else
-    {
-      cdo_abort("Unsupported grid: %s", gridNamePtr(gridtype));
-    }
+  else if (gridtype == GRID_GENERIC) {}
+  else if (gridtype == GRID_CURVILINEAR) { gen_boxgrid_curv2D(gridID1, xinc, yinc, gridID2); }
+  else { cdo_abort("Unsupported grid: %s", gridNamePtr(gridtype)); }
 
   return gridID2;
 }
@@ -268,7 +257,7 @@ gridbox_stat(const Field &field1, Field &field2, size_t xinc, size_t yinc, int s
       auto ompthID = cdo_omp_get_thread_num();
 
       auto &field = fields[ompthID];
-      field.nmiss = 0;
+      field.numMissVals = 0;
 
       auto ilat = ig / nlon2;
       auto ilon = ig - ilat * nlon2;
@@ -284,7 +273,7 @@ gridbox_stat(const Field &field1, Field &field2, size_t xinc, size_t yinc, int s
               auto index = jj * nlon1 + ii;
               if (ii >= nlon1) break;
               field.vec_d[isize] = (field1.memType == MemType::Float) ? (double) field1.vec_f[index] : field1.vec_d[index];
-              if (dbl_is_equal(field.vec_d[isize], field.missval)) field.nmiss++;
+              if (dbl_is_equal(field.vec_d[isize], field.missval)) field.numMissVals++;
               if (useWeight) field.weightv[isize] = field1.weightv[index];
               isize++;
             }
@@ -301,28 +290,31 @@ gridbox_stat(const Field &field1, Field &field2, size_t xinc, size_t yinc, int s
   field_num_mv(field2);
 }
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("gridboxrange",  FieldFunc_Range,  0, nullptr);
-  cdo_operator_add("gridboxmin",    FieldFunc_Min,    0, nullptr);
-  cdo_operator_add("gridboxmax",    FieldFunc_Max,    0, nullptr);
-  cdo_operator_add("gridboxsum",    FieldFunc_Sum,    0, nullptr);
-  cdo_operator_add("gridboxmean",   FieldFunc_Meanw,  1, nullptr);
-  cdo_operator_add("gridboxavg",    FieldFunc_Avgw,   1, nullptr);
-  cdo_operator_add("gridboxvar",    FieldFunc_Varw,   1, nullptr);
-  cdo_operator_add("gridboxvar1",   FieldFunc_Var1w,  1, nullptr);
-  cdo_operator_add("gridboxstd",    FieldFunc_Stdw,   1, nullptr);
-  cdo_operator_add("gridboxstd1",   FieldFunc_Std1w,  1, nullptr);
-  cdo_operator_add("gridboxskew",   FieldFunc_Skew,   0, nullptr);
-  cdo_operator_add("gridboxkurt",   FieldFunc_Kurt,   0, nullptr);
-  cdo_operator_add("gridboxmedian", FieldFunc_Median, 0, nullptr);
-  // clang-format on
-}
-
-class ModuleGridboxstat
+class Gridboxstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Gridboxstat",
+    .operators = { { "gridboxrange", FieldFunc_Range, 0, GridboxstatHelp },
+                   { "gridboxmin", FieldFunc_Min, 0, GridboxstatHelp },
+                   { "gridboxmax", FieldFunc_Max, 0, GridboxstatHelp },
+                   { "gridboxsum", FieldFunc_Sum, 0, GridboxstatHelp },
+                   { "gridboxmean", FieldFunc_Meanw, 1, GridboxstatHelp },
+                   { "gridboxavg", FieldFunc_Avgw, 1, GridboxstatHelp },
+                   { "gridboxstd", FieldFunc_Stdw, 1, GridboxstatHelp },
+                   { "gridboxstd1", FieldFunc_Std1w, 1, GridboxstatHelp },
+                   { "gridboxvar", FieldFunc_Varw, 1, GridboxstatHelp },
+                   { "gridboxvar1", FieldFunc_Var1w, 1, GridboxstatHelp },
+                   { "gridboxskew", FieldFunc_Skew, 0, GridboxstatHelp },
+                   { "gridboxkurt", FieldFunc_Kurt, 0, GridboxstatHelp },
+                   { "gridboxmedian", FieldFunc_Median, 0, GridboxstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Gridboxstat> registration = RegisterEntry<Gridboxstat>(module);
   int lastgrid = -1;
   bool wstatus = false;
 
@@ -344,17 +336,14 @@ class ModuleGridboxstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("xinc, yinc");
     operator_check_argc(2);
     xinc = parameter_to_int(cdo_operator_argv(0));
     yinc = parameter_to_int(cdo_operator_argv(1));
 
-    add_operators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -434,18 +423,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Gridboxstat(void *process)
-{
-  ModuleGridboxstat gridboxstat;
-  gridboxstat.init(process);
-  gridboxstat.run();
-  gridboxstat.close();
-
-  return nullptr;
-}
diff --git a/src/Gridcell.cc b/src/Gridcell.cc
index 83bb357875a61cd03690e57be94737f242966ef2..ecf4d60197817c105436a814e27ba84d2139e8f4 100644
--- a/src/Gridcell.cc
+++ b/src/Gridcell.cc
@@ -20,34 +20,52 @@
 #include "cdo_zaxis.h"
 #include <mpim_grid.h>
 #include "constants.h"
+#include "param_conversion.h"
+#include "pmlist.h"
 
 double gridGetPlanetRadius(int gridID);
 
 double
-getPlanetRadius(int gridID)
+planet_radius_env(void)
 {
-  static auto radiusFromEnv = true;
-  if (radiusFromEnv)
+  static double planetRadiusEnv{ 0 };
+  static auto readFromEnv = true;
+  if (readFromEnv)
     {
-      radiusFromEnv = false;
+      readFromEnv = false;
       auto envstr = getenv("PLANET_RADIUS");
       if (envstr)
         {
-          auto fval = atof(envstr);
-          if (fval > 0) PlanetRadius = fval;
+          auto fval = radius_str_to_meter(envstr);
+          if (is_not_equal(fval, 0)) planetRadiusEnv = fval;
         }
     }
 
-  auto planetRadius = PlanetRadius;
+  return planetRadiusEnv;
+}
+
+double
+get_planet_radius_in_meter(int gridID)
+{
+  auto planetRadiusInMeter{ PlanetRadiusDefault };
 
-  auto gridRadius = gridGetPlanetRadius(gridID);
-  if (gridRadius > 1) planetRadius = gridRadius;
+  if (is_not_equal(planet_radius_env(), 0))
+    {
+      planetRadiusInMeter = planet_radius_env();
+      cdo_print("Using planet radius from env.var. PLANET_RADIUS: %.8gm", planetRadiusInMeter);
+    }
+  else if (gridGetPlanetRadius(gridID) > 1)
+    {
+      planetRadiusInMeter = gridGetPlanetRadius(gridID);
+      cdo_print("Using planet radius from grid description: %.8gm", planetRadiusInMeter);
+    }
+  else { cdo_print("Using default planet radius: %.8gm", planetRadiusInMeter); }
 
-  return planetRadius;
+  return planetRadiusInMeter;
 }
 
-void
-gridcell_areas(int gridID, Varray<double> &array)
+static void
+gridcell_areas(int gridID, Varray<double> &array, double planetRadiusInMeter)
 {
   auto gridtype = gridInqType(gridID);
 
@@ -56,29 +74,36 @@ gridcell_areas(int gridID, Varray<double> &array)
     {
       if (gridHasArea(gridID))
         {
-          if (Options::cdoVerbose) cdo_print("Using existing grid cell area!");
+          cdo_print("Using existing grid cell area!");
           gridInqArea(gridID, array.data());
         }
       else
         {
           auto status = gridGenArea(gridID, array.data());
-          if (status == 1)
-            cdo_abort("%s: Cell corner coordinates missing!", __func__);
-          else if (status == 2)
-            cdo_abort("%s: Can't compute grid cell area for this grid!", __func__);
+          // clang-format off
+          if      (status == 1) cdo_abort("%s: Cell corner coordinates missing!", __func__);
+          else if (status == 2) cdo_abort("%s: Can't compute grid cell area for this grid!", __func__);
+          // clang-format on
 
-          auto planetRadius = getPlanetRadius(gridID);
-          if (Options::cdoVerbose) cdo_print("Planet radius: %.2f", planetRadius);
+          if (planetRadiusInMeter <= 0) planetRadiusInMeter = get_planet_radius_in_meter(gridID);
           auto ngp = gridInqSize(gridID);
-          for (size_t i = 0; i < ngp; ++i) array[i] *= planetRadius * planetRadius;
+          for (size_t i = 0; i < ngp; ++i) array[i] *= planetRadiusInMeter * planetRadiusInMeter;
         }
     }
   else { cdo_abort("%s: Unsupported grid type: %s", __func__, gridNamePtr(gridtype)); }
 }
 
+void
+gridcell_areas(int gridID, Varray<double> &array)
+{
+  gridcell_areas(gridID, array, 0.0);
+}
+
 static void
-grid_dx(int gridID, Varray<double> &array)
+grid_dx(int gridID, Varray<double> &array, double planetRadiusInMeter)
 {
+  if (planetRadiusInMeter <= 0) planetRadiusInMeter = get_planet_radius_in_meter(gridID);
+
   auto gridsize = gridInqSize(gridID);
   auto xsize = gridInqXsize(gridID);
   auto ysize = gridInqYsize(gridID);
@@ -93,9 +118,6 @@ grid_dx(int gridID, Varray<double> &array)
   cdo_grid_to_radian(gridID, CDI_XAXIS, xv, "grid longitudes");
   cdo_grid_to_radian(gridID, CDI_YAXIS, yv, "grid latitudes");
 
-  auto planetRadius = getPlanetRadius(gridID);
-  if (Options::cdoVerbose) cdo_print("Planet radius: %.2f", planetRadius);
-
   for (size_t j = 0; j < ysize; ++j)
     {
       auto joff = j * xsize;
@@ -118,14 +140,16 @@ grid_dx(int gridID, Varray<double> &array)
               len2 = orthodrome(xv[joff + i], yv[joff + i], xv[joff + i + 1], yv[joff + i + 1]);
             }
 
-          array[joff + i] = 0.5 * (len1 + len2) * planetRadius;
+          array[joff + i] = 0.5 * (len1 + len2) * planetRadiusInMeter;
         }
     }
 }
 
 static void
-grid_dy(int gridID, Varray<double> &array)
+grid_dy(int gridID, Varray<double> &array, double planetRadiusInMeter)
 {
+  if (planetRadiusInMeter <= 0) planetRadiusInMeter = get_planet_radius_in_meter(gridID);
+
   auto gridsize = gridInqSize(gridID);
   auto xsize = gridInqXsize(gridID);
   auto ysize = gridInqYsize(gridID);
@@ -140,9 +164,6 @@ grid_dy(int gridID, Varray<double> &array)
   cdo_grid_to_radian(gridID, CDI_XAXIS, xv, "grid longitudes");
   cdo_grid_to_radian(gridID, CDI_YAXIS, yv, "grid latitudes");
 
-  auto planetRadius = getPlanetRadius(gridID);
-  if (Options::cdoVerbose) cdo_print("Planet radius: %.2f", planetRadius);
-
   for (size_t i = 0; i < xsize; ++i)
     {
       for (size_t j = 0; j < ysize; ++j)
@@ -167,16 +188,61 @@ grid_dy(int gridID, Varray<double> &array)
               len2 = orthodrome(xv[joff + i], yv[joff + i], xv[joffp1 + i], yv[joffp1 + i]);
             }
 
-          array[joff + i] = 0.5 * (len1 + len2) * planetRadius;
+          array[joff + i] = 0.5 * (len1 + len2) * planetRadiusInMeter;
         }
     }
 }
 
-class ModuleGridcell
+static void
+get_parameter(double &radiusInMeter)
 {
+  auto pargc = cdo_operator_argc();
+  if (pargc)
+    {
+      auto &pargv = cdo_get_oper_argv();
+
+      KVList kvlist;
+      kvlist.name = cdo_module_name();
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
+      if (Options::cdoVerbose) kvlist.print();
+
+      for (const auto &kv : kvlist)
+        {
+          const auto &key = kv.key;
+          if (kv.nvalues > 1) cdo_abort("Too many values for parameter key >%s<!", key);
+          if (kv.nvalues < 1) cdo_abort("Missing value for parameter key >%s<!", key);
+          const auto &value = kv.values[0];
+
+          // clang-format off
+          if (key == "radius") radiusInMeter = radius_str_to_meter(value);
+          else cdo_abort("Invalid parameter key >%s<!", key);
+          // clang-format on
+        }
+    }
+}
+
+class Gridcell : public Process
+{
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Gridcell",
+    .operators = { { "gridarea", 1, 0, GridcellHelp },
+                   { "gridweights", GridcellHelp },
+                   { "gridmask", GridcellHelp },
+                   { "griddx", 1, 0, GridcellHelp },
+                   { "griddy", 1, 0, GridcellHelp },
+                   { "gridcellidx", GridcellHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Gridcell> registration = RegisterEntry<Gridcell>(module);
+
+  int GRIDAREA, GRIDWEIGHTS, GRIDMASK, GRIDDX, GRIDDY, GRIDCELLIDX;
 
   CdoStreamID streamID1;
-  CdoStreamID streamID2;
 
   int operatorID;
   int gridID;
@@ -184,29 +250,38 @@ class ModuleGridcell
 
   size_t gridsize;
 
+  double planetRadiusInMeter{ 0 };
+
   Varray<double> array;
-  int GRIDAREA, GRIDWEIGHTS, GRIDMASK, GRIDDX, GRIDDY, GRIDCELLIDX;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     // clang-format off
-    GRIDAREA   = cdo_operator_add("gridarea",     1,  0, nullptr);
-    GRIDWEIGHTS= cdo_operator_add("gridweights",  1,  0, nullptr);
-    GRIDMASK   = cdo_operator_add("gridmask",     0,  0, nullptr);
-    GRIDDX     = cdo_operator_add("griddx",       1,  0, nullptr);
-    GRIDDY     = cdo_operator_add("griddy",       1,  0, nullptr);
-    GRIDCELLIDX= cdo_operator_add("gridcellidx",  0,  0, nullptr);
+    GRIDAREA    = module.get_id("gridarea");
+    GRIDWEIGHTS = module.get_id("gridweights");
+    GRIDMASK    = module.get_id("gridmask");
+    GRIDDX      = module.get_id("griddx");
+    GRIDDY      = module.get_id("griddy");
+    GRIDCELLIDX = module.get_id("gridcellidx");
     // clang-format on
 
     operatorID = cdo_operator_id();
 
-    operator_check_argc(0);
+    auto needRadius = (cdo_operator_f1(operatorID) > 0);
 
-    // const bool needRadius = cdo_operator_f1(operatorID) > 0;
+    if (needRadius && cdo_operator_argc() == 1)
+      {
+        double radiusInMeter{ 0 };
+        get_parameter(radiusInMeter);
+        if (is_not_equal(radiusInMeter, 0))
+          {
+            planetRadiusInMeter = radiusInMeter;
+            cdo_print("Using user defined planet radius: %.8gm", planetRadiusInMeter);
+          }
+      }
+    else { operator_check_argc(0); }
 
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
@@ -269,7 +344,7 @@ public:
   void
   run()
   {
-    if (operatorID == GRIDAREA) { gridcell_areas(gridID, array); }
+    if (operatorID == GRIDAREA) { gridcell_areas(gridID, array, planetRadiusInMeter); }
     else if (operatorID == GRIDWEIGHTS)
       {
         auto status = gridcell_weights(gridID, array);
@@ -293,35 +368,22 @@ public:
           {
             if (gridtype != GRID_CURVILINEAR) gridID = gridToCurvilinear(gridID, NeedCorners::Yes);
 
-            (operatorID == GRIDDX) ? grid_dx(gridID, array) : grid_dy(gridID, array);
+            (operatorID == GRIDDX) ? grid_dx(gridID, array, planetRadiusInMeter) : grid_dy(gridID, array, planetRadiusInMeter);
           }
         else { cdo_abort("Unsupported grid type: %s", gridNamePtr(gridtype)); }
       }
 
-    streamID2 = cdo_open_write(1);
+    auto streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
     cdo_def_timestep(streamID2, 0);
     cdo_def_record(streamID2, 0, 0);
     cdo_write_record(streamID2, &array[0], 0);
+    cdo_stream_close(streamID2);
   }
 
   void
   close()
   {
-    cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Gridcell(void *process)
-{
-  ModuleGridcell gridcell;
-  gridcell.init(process);
-  gridcell.run();
-  gridcell.close();
-
-  return nullptr;
-}
diff --git a/src/Gridsearch.cc b/src/Gridsearch.cc
index 94312a32019944e99445bd2276d15932690d46e5..3af169b697002be467d4aa9f5bd93ffdaa569562 100644
--- a/src/Gridsearch.cc
+++ b/src/Gridsearch.cc
@@ -110,7 +110,8 @@ boundbox_from_corners1r(long ic, long nc, const Varray<double> &cornerLon, const
 }
 
 static void
-boundbox_from_corners(long size, long nc, const Varray<double> &cornerLon, const Varray<double> &cornerLat, Varray<float> &bound_box)
+boundbox_from_corners(long size, long nc, const Varray<double> &cornerLon, const Varray<double> &cornerLat,
+                      Varray<float> &bound_box)
 {
   for (long i = 0; i < size; ++i)
     {
@@ -215,23 +216,32 @@ cell_search(int gridIDsrc, int gridIDtgt)
   grid_delete(tgtGrid);
 }
 
-class ModuleGridsearch
+class Gridsearch : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Gridsearch",
+    .operators = { { "testpointsearch"}, { "testcellsearch"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Gridsearch> registration = RegisterEntry<Gridsearch>(module);
+  int TESTPOINTSEARCH, TESTCELLSEARCH;
   int operatorID;
 
   int gridID1;
   int gridID2;
 
-  int TESTCELLSEARCH, TESTPOINTSEARCH;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    TESTPOINTSEARCH = cdo_operator_add("testpointsearch", 0, 0, nullptr);
-    TESTCELLSEARCH = cdo_operator_add("testcellsearch", 0, 0, nullptr);
+    TESTPOINTSEARCH = module.get_id("testpointsearch");
+    TESTCELLSEARCH = module.get_id("testcellsearch");
 
     operatorID = cdo_operator_id();
 
@@ -251,17 +261,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Gridsearch(void *process)
-{
-  ModuleGridsearch gridsearch;
-  gridsearch.init(process);
-  gridsearch.run();
-  gridsearch.close();
-
-  return nullptr;
-}
diff --git a/src/Harmonic.cc b/src/Harmonic.cc
index 161d3efdbe78ecd270e04104bb7c8047a7393971..ce4846a323066065330805f7a5034e5a484d7ec2 100644
--- a/src/Harmonic.cc
+++ b/src/Harmonic.cc
@@ -20,8 +20,20 @@
 #include "util_files.h"
 #include "util_string.h"
 
-class ModuleHarmonic
+class Harmonic : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Harmonic",
+    .operators = { { "harmonic"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Harmonic> registration = RegisterEntry<Harmonic>(module);
+
   CdiDateTime vDateTime{};
 
   CdoStreamID streamID1;
@@ -44,10 +56,8 @@ class ModuleHarmonic
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     operator_input_arg("wave number and wave length of first harmonic in number of timesteps");
 
     operator_check_argc(2);
@@ -102,10 +112,8 @@ public:
         work[j].resize(nvars);
         for (int varID = 0; varID < nvars; ++varID)
           {
-            auto gridsize = varList1[varID].gridsize;
-            auto nlevels = varList1[varID].nlevels;
-            work[j][varID].resize(gridsize * nlevels);
-            varray_fill(gridsize * nlevels, work[j][varID], 0.0);
+            const auto &var = varList1[varID];
+            work[j][varID].resize(var.gridsize * var.nlevels, 0);
           }
       }
 
@@ -128,10 +136,10 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
-            if (nmiss) cdo_abort("Missing values are not allowed!");
+            if (numMissVals) cdo_abort("Missing values are not allowed!");
 
             auto gridsize = varList1[varID].gridsize;
             auto offset = gridsize * levelID;
@@ -162,12 +170,11 @@ public:
       {
         for (int varID = 0; varID < nvars; ++varID)
           {
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevels = varList2[varID].nlevels;
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            const auto &var2 = varList2[varID];
+            for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
-                auto offset = gridsize * levelID;
-                for (size_t i = 0; i < gridsize; ++i)
+                auto offset = var2.gridsize * levelID;
+                for (size_t i = 0; i < var2.gridsize; ++i)
                   {
                     auto sqrwork1 = work[j][varID][i + offset] * work[j][varID][i + offset];
                     auto sqrwork2 = work[n_out + j][varID][i + offset] * work[n_out + j][varID][i + offset];
@@ -181,12 +188,11 @@ public:
       {
         for (int varID = 0; varID < nvars; ++varID)
           {
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevels = varList2[varID].nlevels;
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            const auto &var2 = varList2[varID];
+            for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
-                auto offset = gridsize * levelID;
-                for (size_t i = 0; i < gridsize; ++i)
+                auto offset = var2.gridsize * levelID;
+                for (size_t i = 0; i < var2.gridsize; ++i)
                   out[n_out - 1][varID][i + offset] = work[2 * n_out - 1][varID][i + offset] / nts;
               }
           }
@@ -202,11 +208,10 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevels = varList2[varID].nlevels;
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            const auto &var2 = varList2[varID];
+            for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
-                auto offset = gridsize * levelID;
+                auto offset = var2.gridsize * levelID;
                 cdo_def_record(streamID2, varID, levelID);
                 cdo_write_record(streamID2, &out[j][varID][offset], 0);
               }
@@ -217,18 +222,16 @@ public:
       {
         for (int varID = 0; varID < nvars; ++varID)
           {
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevels = varList2[varID].nlevels;
-            auto missval = vlistInqVarMissval(vlistID2, varID);
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            const auto &var2 = varList2[varID];
+            for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
-                auto offset = gridsize * levelID;
-                for (size_t i = 0; i < gridsize; ++i)
+                auto offset = var2.gridsize * levelID;
+                for (size_t i = 0; i < var2.gridsize; ++i)
                   {
                     auto work1 = work[j][varID][i + offset];
                     auto work2 = work[n_out + j][varID][i + offset];
                     auto tmpatan2 = std::atan2(work1, work2) * n / (j + 1) / 2 / M_PI;
-                    out[j][varID][i + offset] = (work1 > 0.0 || work2 > 0.0) ? tmpatan2 : missval;
+                    out[j][varID][i + offset] = (work1 > 0.0 || work2 > 0.0) ? tmpatan2 : var2.missval;
                     if (out[j][varID][i + offset] < 0) out[j][varID][i + offset] += n / (j + 1.);
                   }
               }
@@ -246,15 +249,13 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevels = varList2[varID].nlevels;
-            auto missval = vlistInqVarMissval(vlistID2, varID);
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            const auto &var2 = varList2[varID];
+            for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
-                auto offset = gridsize * levelID;
-                auto nmiss = array_num_mv(gridsize, &out[j][varID][offset], missval);
+                auto offset = var2.gridsize * levelID;
+                auto numMissVals = array_num_mv(var2.gridsize, &out[j][varID][offset], var2.missval);
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, &out[j][varID][offset], nmiss);
+                cdo_write_record(streamID2, &out[j][varID][offset], numMissVals);
               }
           }
       }
@@ -264,19 +265,5 @@ public:
   close()
   {
     for (int j = 0; j < n_out; ++j) cdo_stream_close(streamIDs[j]);
-
-    cdo_finish();
   }
 };
-
-void *
-Harmonic(void *process)
-{
-  ModuleHarmonic harmonic;
-
-  harmonic.init(process);
-  harmonic.run();
-  harmonic.close();
-
-  return nullptr;
-}
diff --git a/src/Healpix.cc b/src/Healpix.cc
index e8f8847a6b50bd1712d020b4ddd14e6785e8d46a..fe27e5b54042291d453f5e6003c347bb091c76e1 100644
--- a/src/Healpix.cc
+++ b/src/Healpix.cc
@@ -110,7 +110,7 @@ degrade(const Varray<T> &v1, size_t gridsize2, Varray<T> &v2, bool hasMissvals,
 static void
 hp_degrade(const Field &field1, Field &field2, const HealpixParams &params)
 {
-  auto hasMissvals = (field1.nmiss > 0);
+  auto hasMissvals = (field1.numMissVals > 0);
   if (field1.memType == MemType::Float)
     degrade(field1.vec_f, field2.gridsize, field2.vec_f, hasMissvals, (float) field1.missval, params);
   else
@@ -140,7 +140,7 @@ upgrade(size_t gridsize1, const Varray<T> &v1, Varray<T> &v2, bool hasMissvals,
 static void
 hp_upgrade(const Field &field1, Field &field2, const HealpixParams &params)
 {
-  auto hasMissvals = (field1.nmiss > 0);
+  auto hasMissvals = (field1.numMissVals > 0);
   if (field1.memType == MemType::Float)
     upgrade(field1.gridsize, field1.vec_f, field2.vec_f, hasMissvals, (float) field1.missval, params);
   else
@@ -208,7 +208,7 @@ get_parameter(void)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -294,8 +294,21 @@ hp_define_grid(int gridID1, HealpixParams &params)
   return gridIDout;
 }
 
-class ModuleHealpix
+class Healpix : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Healpix",
+    .operators = { { "hpupgrade", HealpixHelp }, { "hpdegrade", HealpixHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Healpix> registration = RegisterEntry<Healpix>(module);
+
+  int HPDEGRADE;
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -315,17 +328,14 @@ class ModuleHealpix
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     // clang-format off
-                   cdo_operator_add("hpupgrade", 0, 0, nullptr);
-  auto HPDEGRADE = cdo_operator_add("hpdegrade", 0, 0, nullptr);
+    HPDEGRADE = module.get_id("hpdegrade");
     // clang-format on
 
     auto operatorID = cdo_operator_id();
-     doDegrade = (operatorID == HPDEGRADE);
+    doDegrade = (operatorID == HPDEGRADE);
 
     params = get_parameter();
     params.doDegrade = doDegrade;
@@ -341,7 +351,7 @@ public:
     auto gridID = vlistGrid(vlistID1, 0);
     if (!is_healpix_grid(gridID)) cdo_abort("Input grid is not healpix!");
 
-     vlistID2 = vlistDuplicate(vlistID1);
+    vlistID2 = vlistDuplicate(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
@@ -354,6 +364,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -388,6 +399,7 @@ public:
         tsID1++;
       }
   }
+
   void
   close()
   {
@@ -396,18 +408,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Healpix(void *process)
-{
-
-  ModuleHealpix healpix;
-  healpix.init(process);
-  healpix.run();
-  healpix.close();
-  return nullptr;
-}
diff --git a/src/Hi.cc b/src/Hi.cc
index 6d879448eb90ff9cdbb76e1641d66e851ce5b847..7ad878a86827665e4a9c26ca04fcec70c9c4016d 100644
--- a/src/Hi.cc
+++ b/src/Hi.cc
@@ -53,7 +53,7 @@ farexpr(Field &field1, const Field &field2, const Field &field3, double (*expres
   auto len = gridInqSize(grid1);
   if (len != gridInqSize(grid2) || len != gridInqSize(grid3)) cdo_abort("Fields have different gridsize (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss || field3.nmiss)
+  if (field1.numMissVals || field2.numMissVals || field3.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (DBL_IS_EQUAL(field1.vec_d[i], missval1) || DBL_IS_EQUAL(field2.vec_d[i], missval2)
@@ -67,11 +67,22 @@ farexpr(Field &field1, const Field &field2, const Field &field3, double (*expres
       for (size_t i = 0; i < len; ++i) field1.vec_d[i] = expression(field1.vec_d[i], field2.vec_d[i], field3.vec_d[i], missval1);
     }
 
-  field1.nmiss = field_num_miss(field1);
+  field1.numMissVals = field_num_miss(field1);
 }
 
-class ModuleHi
+class Hi : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Hi",
+    .operators = { { "hi"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Hi> registration = RegisterEntry<Hi>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -92,11 +103,8 @@ class ModuleHi
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("hi", 0, 0, nullptr);
 
     operator_check_argc(0);
 
@@ -166,15 +174,15 @@ public:
           {
             int varID1, levelID1;
             cdo_inq_record(streamID1, &varID1, &levelID1);
-            cdo_read_record(streamID1, field1.vec_d.data(), &field1.nmiss);
+            cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
 
             int varID2, levelID2;
             cdo_inq_record(streamID2, &varID2, &levelID2);
-            cdo_read_record(streamID2, field2.vec_d.data(), &field2.nmiss);
+            cdo_read_record(streamID2, field2.vec_d.data(), &field2.numMissVals);
 
             int varID3, levelID3;
             cdo_inq_record(streamID3, &varID3, &levelID3);
-            cdo_read_record(streamID3, field3.vec_d.data(), &field3.nmiss);
+            cdo_read_record(streamID3, field3.vec_d.data(), &field3.numMissVals);
 
             if (varID1 != varID2 || varID1 != varID3 || levelID1 != levelID2 || levelID1 != levelID3)
               cdo_abort("Input streams have different structure!");
@@ -193,7 +201,7 @@ public:
             farexpr(field1, field2, field3, humidityIndex);
 
             cdo_def_record(streamID4, varID4, levelID1);
-            cdo_write_record(streamID4, field1.vec_d.data(), field1.nmiss);
+            cdo_write_record(streamID4, field1.vec_d.data(), field1.numMissVals);
           }
 
         tsID++;
@@ -207,18 +215,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Hi(void *process)
-{
-  ModuleHi hi;
-  hi.init(process);
-  hi.run();
-  hi.close();
-
-  return nullptr;
-}
diff --git a/src/Histogram.cc b/src/Histogram.cc
index 49e0a8b9e8170c735feeae86bd877720ddefa493..df1b7ba3000516bb1762dfa7d240251ccf6a24b7 100644
--- a/src/Histogram.cc
+++ b/src/Histogram.cc
@@ -17,8 +17,24 @@
 #include "process_int.h"
 #include "param_conversion.h"
 
-class ModuleHistogram
+class Histogram : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Histogram",
+    .operators = { { "histcount", HistogramHelp },
+                   { "histsum", HistogramHelp },
+                   { "histmean", HistogramHelp },
+                   { "histfreq", HistogramHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Histogram> registration = RegisterEntry<Histogram>(module);
+  int HISTCOUNT, HISTSUM, HISTMEAN, HISTFREQ;
+
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -39,22 +55,16 @@ private:
   int nvars;
   int operatorID;
 
-  int HISTCOUNT;
-  int HISTSUM;
-  int HISTMEAN;
-  int HISTFREQ;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    HISTCOUNT = cdo_operator_add("histcount", 0, 0, nullptr);
-    HISTSUM   = cdo_operator_add("histsum",   0, 0, nullptr);
-    HISTMEAN  = cdo_operator_add("histmean",  0, 0, nullptr);
-    HISTFREQ  = cdo_operator_add("histfreq",  0, 0, nullptr);
+HISTCOUNT = module.get_id("histcount");
+HISTSUM = module.get_id("histsum");
+HISTMEAN = module.get_id("histmean");
+HISTFREQ = module.get_id("histfreq");
     // clang-format on
 
     (void) (HISTSUM);  // unused
@@ -143,13 +153,13 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             const auto gridsize = varList1[varID].gridsize;
             const auto missval = varList1[varID].missval;
 
-            nmiss = 0;
+            numMissVals = 0;
             for (size_t i = 0; i < gridsize; ++i)
               {
                 if (!DBL_IS_EQUAL(array[i], missval))
@@ -171,7 +181,7 @@ public:
                   }
                 else
                   {
-                    nmiss++;  // missing value
+                    numMissVals++;  // missing value
                   }
               }
           }
@@ -188,7 +198,7 @@ public:
 
         for (int index = 0; index < nbins; ++index)
           {
-            size_t nmiss = 0;
+            size_t numMissVals = 0;
             const auto offset = gridsize * index;
 
             for (size_t i = 0; i < gridsize; ++i)
@@ -208,7 +218,7 @@ public:
                   }
                 else
                   {
-                    nmiss++;
+                    numMissVals++;
                     varcount[varID][offset + i] = missval;
                     vardata[varID][offset + i] = missval;
                   }
@@ -217,9 +227,9 @@ public:
             cdo_def_record(streamID2, varID, index);
 
             if (operatorID == HISTCOUNT)
-              cdo_write_record(streamID2, &varcount[varID][offset], nmiss);
+              cdo_write_record(streamID2, &varcount[varID][offset], numMissVals);
             else
-              cdo_write_record(streamID2, &vardata[varID][offset], nmiss);
+              cdo_write_record(streamID2, &vardata[varID][offset], numMissVals);
           }
       }
   }
@@ -229,17 +239,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Histogram(void *process)
-{
-  ModuleHistogram histogram;
-  histogram.init(process);
-  histogram.run();
-  histogram.close();
-  return nullptr;
-}
diff --git a/src/Importamsr.cc b/src/Importamsr.cc
index f7123b35aa55194f5955e92f0e1a8dac3437144d..bb2f57d6f86fe6555fb4387bd92fe78254ceda90 100644
--- a/src/Importamsr.cc
+++ b/src/Importamsr.cc
@@ -34,12 +34,12 @@ init_amsr_day(int vlistID, int gridID, int zaxisID, int nvars)
   */
   const char *name[] = { "hours", "sst", "wind", "vapor", "cloud", "rain" };
   const char *units[] = { "h", "deg Celcius", "m/s", "mm", "mm", "mm/h" };
-  const double xscale[] = { 0.1, 0.15, 0.2, 0.3, 0.01, 0.1 };
-  const double xminval[] = { 0., -3., 0., 0., 0., 0. };
+  constexpr double xscale[] = { 0.1, 0.15, 0.2, 0.3, 0.01, 0.1 };
+  constexpr double xminval[] = { 0., -3., 0., 0., 0., 0. };
 
   for (int i = 0; i < nvars; ++i)
     {
-      const int varID = vlistDefVar(vlistID, gridID, zaxisID, TIME_VARYING);
+      int varID = vlistDefVar(vlistID, gridID, zaxisID, TIME_VARYING);
       cdiDefKeyString(vlistID, varID, CDI_KEY_NAME, name[i]);
       cdiDefKeyString(vlistID, varID, CDI_KEY_UNITS, units[i]);
       vlistDefVarDatatype(vlistID, varID, CDI_DATATYPE_INT16);
@@ -78,13 +78,13 @@ init_amsr_averaged(int vlistID, int gridID, int zaxisID, int nvars)
   */
   const char *name[] = { "sst", "wind", "vapor", "cloud", "rain" };
   const char *units[] = { "deg Celcius", "m/s", "mm", "mm", "mm/h" };
-  const double xscale[] = { 0.15, 0.2, 0.3, 0.01, 0.1 };
-  const double xminval[] = { -3., 0., 0., 0., 0. };
+  constexpr double xscale[] = { 0.15, 0.2, 0.3, 0.01, 0.1 };
+  constexpr double xminval[] = { -3., 0., 0., 0., 0. };
 
   for (int i = 0; i < nvars; ++i)
     {
       // varID = vlistDefVar(vlistID, gridID, zaxisID, TIME_CONSTANT);
-      const int varID = vlistDefVar(vlistID, gridID, zaxisID, TIME_VARYING);
+      int varID = vlistDefVar(vlistID, gridID, zaxisID, TIME_VARYING);
       cdiDefKeyString(vlistID, varID, CDI_KEY_NAME, name[i]);
       cdiDefKeyString(vlistID, varID, CDI_KEY_UNITS, units[i]);
       vlistDefVarDatatype(vlistID, varID, CDI_DATATYPE_INT16);
@@ -95,42 +95,42 @@ init_amsr_averaged(int vlistID, int gridID, int zaxisID, int nvars)
 }
 
 static void
-read_amsr(FILE *fp, int vlistID, int nvars, Varray2D<double> &data, size_t *nmiss)
+read_amsr(FILE *fp, int vlistID, int nvars, Varray2D<double> &data, size_t *numMissVals)
 {
   std::vector<unsigned char> amsr_data;
 
   for (int varID = 0; varID < nvars; ++varID)
     {
-      const size_t gridsize = gridInqSize(vlistInqVarGrid(vlistID, varID));
+      size_t gridsize = gridInqSize(vlistInqVarGrid(vlistID, varID));
       amsr_data.resize(gridsize);
-      const size_t size = fread(amsr_data.data(), 1, gridsize, fp);
+      size_t size = fread(amsr_data.data(), 1, gridsize, fp);
       if (size != gridsize) cdo_abort("Read error!");
 
-      const double missval = vlistInqVarMissval(vlistID, varID);
+      double missval = vlistInqVarMissval(vlistID, varID);
       double xminval = 0.0, xscale = 1.0;
       cdiInqKeyFloat(vlistID, varID, CDI_KEY_SCALEFACTOR, &xscale);
       cdiInqKeyFloat(vlistID, varID, CDI_KEY_ADDOFFSET, &xminval);
 
-      nmiss[varID] = 0;
+      numMissVals[varID] = 0;
       for (size_t i = 0; i < gridsize; ++i)
         {
           if (amsr_data[i] <= 250) { data[varID][i] = amsr_data[i] * xscale + xminval; }
           else
             {
               data[varID][i] = missval;
-              nmiss[varID]++;
+              numMissVals[varID]++;
             }
         }
     }
 }
 
 static void
-write_data(CdoStreamID streamID, int nvars, Varray2D<double> &data, size_t *nmiss)
+write_data(CdoStreamID streamID, int nvars, Varray2D<double> &data, size_t *numMissVals)
 {
   for (int varID = 0; varID < nvars; ++varID)
     {
       cdo_def_record(streamID, varID, 0);
-      cdo_write_record(streamID, data[varID].data(), nmiss[varID]);
+      cdo_write_record(streamID, data[varID].data(), numMissVals[varID]);
     }
 }
 
@@ -138,18 +138,30 @@ static int
 get_date(const char *name)
 {
   const char *pname = strchr(name, '_');
-  const int date = pname ? atoi(pname + 1) : 0;
+  int date = pname ? atoi(pname + 1) : 0;
   return date;
 }
 
-class ModuleImportamsr
+class Importamsr : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Importamsr",
+    .operators = { { "import_amsr", ImportamsrHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Importamsr> registration = RegisterEntry<Importamsr>(module);
+
   int tsID;
   int nvars;
   int vtime = 0;
   double xvals[NLON], yvals[NLAT];
   Varray2D<double> data;
-  size_t nmiss[MAX_VARS];
+  size_t numMissVals[MAX_VARS];
 
   CdoStreamID streamID;
 
@@ -184,9 +196,9 @@ class ModuleImportamsr
         vtime += 120000;  // 13:30:00
         cdo_def_timestep(streamID, tsID);
 
-        read_amsr(fp, vlistID, nvars, data, nmiss);
+        read_amsr(fp, vlistID, nvars, data, numMissVals);
 
-        write_data(streamID, nvars, data, nmiss);
+        write_data(streamID, nvars, data, numMissVals);
       }
   }
 
@@ -208,18 +220,15 @@ class ModuleImportamsr
     tsID = 0;
     cdo_def_timestep(streamID, tsID);
 
-    read_amsr(fp, vlistID, nvars, data, nmiss);
+    read_amsr(fp, vlistID, nvars, data, numMissVals);
 
-    write_data(streamID, nvars, data, nmiss);
+    write_data(streamID, nvars, data, numMissVals);
   }
 
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     fp = std::fopen(cdo_get_stream_name(0), "r");
@@ -259,6 +268,7 @@ public:
 
     data = Varray2D<double>(MAX_VARS);
   }
+
   void
   run()
   {
@@ -267,6 +277,7 @@ public:
     else { cdo_abort("Unexpected file size for AMSR data!"); }
     process_def_var_num(vlistNvars(vlistID));
   }
+
   void
   close()
   {
@@ -278,19 +289,5 @@ public:
     gridDestroy(gridID);
     zaxisDestroy(zaxisID);
     taxisDestroy(taxisID);
-
-    cdo_finish();
   }
 };
-
-void *
-Importamsr(void *process)
-{
-  ModuleImportamsr importamsr;
-
-  importamsr.init(process);
-  importamsr.run();
-  importamsr.close();
-
-  return nullptr;
-}
diff --git a/src/Importbinary.cc b/src/Importbinary.cc
index 1a80b77acaa58a39129afd2da0ae1b02060fa218..096a1845134b12524f9b6e2414c867f761bb100c 100644
--- a/src/Importbinary.cc
+++ b/src/Importbinary.cc
@@ -110,9 +110,20 @@ define_level(dsets_t *pfi, int nlev)
   return zaxisID;
 }
 
-class ModuleImportbinary
+class Importbinary : public Process
 {
-  size_t nmiss = 0;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Importbinary",
+    .operators = { { "import_binary", ImportbinaryHelp } },
+    .aliases = { { "import_grads", "import_binary" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Importbinary> registration = RegisterEntry<Importbinary>(module);
+  size_t numMissVals = 0;
   int told, fnum;
   int tmin = 0, tmax = 0;
   char *ch = nullptr;
@@ -142,11 +153,9 @@ class ModuleImportbinary
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     dsets_init(&pfi);
@@ -162,13 +171,13 @@ public:
 
     if (nvars == 0) cdo_abort("No variables found!");
 
-     gridID = define_grid(&pfi);
-     zaxisID = define_level(&pfi, 0);
+    gridID = define_grid(&pfi);
+    zaxisID = define_level(&pfi, 0);
 
     auto zaxisIDsfc = zaxisCreate(ZAXIS_SURFACE, 1);
     zaxisDefLevels(zaxisIDsfc, &sfclevel);
 
-     vlistID = vlistCreate();
+    vlistID = vlistCreate();
 
     Varray<int> var_zaxisID(nvars);
     var_dfrm = Varray<int>(nrecs);
@@ -273,12 +282,12 @@ public:
     rDateTime.time = cdiTime_encode(dtim.hr, dtim.mn, 0, 0);
 
     auto calendar = CALENDAR_STANDARD;
-     taxisID = cdo_taxis_create(TAXIS_RELATIVE);
+    taxisID = cdo_taxis_create(TAXIS_RELATIVE);
     taxisDefCalendar(taxisID, calendar);
     taxisDefRdatetime(taxisID, rDateTime);
     vlistDefTaxis(vlistID, taxisID);
 
-     streamID = cdo_open_write(1);
+    streamID = cdo_open_write(1);
     cdo_def_vlist(streamID, vlistID);
 
     gridsize = pfi.dnum[0] * pfi.dnum[1];
@@ -447,18 +456,18 @@ public:
 
                 double fmin = 1.e99;
                 double fmax = -1.e99;
-                nmiss = 0;
+                numMissVals = 0;
                 for (size_t i = 0; i < gridsize; ++i)
                   {
                     if (array[i] > pfi.ulow && array[i] < pfi.uhi)
                       {
                         array[i] = pfi.undef;
-                        nmiss++;
+                        numMissVals++;
                       }
                     else if (std::isnan(array[i]))
                       {
                         array[i] = pfi.undef;
-                        nmiss++;
+                        numMissVals++;
                       }
                     else
                       {
@@ -469,7 +478,7 @@ public:
 
                 auto [varID, levelID] = recList[recID].get();
                 cdo_def_record(streamID, varID, levelID);
-                cdo_write_record(streamID, array.data(), nmiss);
+                cdo_write_record(streamID, array.data(), numMissVals);
               }
           }
 
@@ -491,19 +500,5 @@ public:
     taxisDestroy(taxisID);
 
     if (pfi.infile) std::fclose(pfi.infile);
-
-    cdo_finish();
   }
 };
-
-void *
-Importbinary(void *process)
-{
-  ModuleImportbinary importbinary;
-
-  importbinary.init(process);
-  importbinary.run();
-  importbinary.close();
-
-  return nullptr;
-}
diff --git a/src/Importcmsaf.cc b/src/Importcmsaf.cc
index 572672e5fcba256f5a146c2b0c463a923d9ec737..5dcc9846517083933097b793a7fe4fa9f8bafcc4 100644
--- a/src/Importcmsaf.cc
+++ b/src/Importcmsaf.cc
@@ -16,10 +16,10 @@
 #endif
 
 #include <cdi.h>
+#include <stdlib.h> // realloc()
 
 #include "cdo_options.h"
 #include "compare.h"
-#include "dmemory.h"
 #include "varray.h"
 #include "process_int.h"
 #include "cdo_default_values.h"
@@ -350,7 +350,7 @@ read_geolocation(hid_t loc_id, int nx, int ny, int lprojtype)
   H5Tinsert(proj_tid, "Reference ellipsoid", HOFFSET(proj_t, ellipsoid), str_tid);
   H5Tinsert(proj_tid, "Projection parameter", HOFFSET(proj_t, parameter), fltarr_tid);
 
-  //if (projection_name) Free(projection_name); failed with: pointer being freed was not allocated
+  // if (projection_name) free(projection_name); failed with: pointer being freed was not allocated
 
   grp_id = H5Gopen(loc_id, "Geolocation");
 
@@ -649,8 +649,8 @@ static void
 read_dataset(hid_t loc_id, const char *name, void *opdata)
 {
   hid_t dataspace;
-  hsize_t dims_out[9]; // dataset dimensions
-  herr_t status;       // Generic return value
+  hsize_t dims_out[9];  // dataset dimensions
+  herr_t status;        // Generic return value
   hid_t attr, atype, atype_mem;
   int iattr;
   float fattr;
@@ -667,9 +667,9 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
   int ftype = 0;
   int len;
   int dtype = CDI_DATATYPE_FLT32;
-  char attstring[4096]; // Buffer to read string attribute back
+  char attstring[4096];  // Buffer to read string attribute back
   char varname[CDI_MAX_NAME];
-  size_t nmiss;
+  size_t numMissVals;
   int num_attrs;
 
   attstring[0] = 0;
@@ -927,19 +927,19 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
           else if (cdo_cmpstr(attname, "description"))
             {
               H5Aread(attr, atype_mem, attstring);
-              if (((datasets_t *) opdata)->obj[nset].description) Free(((datasets_t *) opdata)->obj[nset].description);
+              if (((datasets_t *) opdata)->obj[nset].description) free(((datasets_t *) opdata)->obj[nset].description);
               ((datasets_t *) opdata)->obj[nset].description = strdup(attstring);
             }
           else if (cdo_cmpstr(attname, "title"))
             {
               H5Aread(attr, atype_mem, attstring);
-              if (((datasets_t *) opdata)->obj[nset].title) Free(((datasets_t *) opdata)->obj[nset].title);
+              if (((datasets_t *) opdata)->obj[nset].title) free(((datasets_t *) opdata)->obj[nset].title);
               ((datasets_t *) opdata)->obj[nset].title = strdup(attstring);
             }
           else if (cdo_cmpstr(attname, "time"))
             {
               H5Aread(attr, atype_mem, attstring);
-              if (((datasets_t *) opdata)->obj[nset].time) Free(((datasets_t *) opdata)->obj[nset].time);
+              if (((datasets_t *) opdata)->obj[nset].time) free(((datasets_t *) opdata)->obj[nset].time);
               ((datasets_t *) opdata)->obj[nset].time = strdup(attstring);
             }
           else if (cdo_cmpstr(attname, "unit"))
@@ -955,7 +955,7 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
 
       int offset = gridsize * (nz - 1);
       double *array = ((datasets_t *) opdata)->obj[nset].array;
-      array = (double *) Realloc(array, gridsize * nz * nt * sizeof(double));
+      array = (double *) realloc(array, gridsize * nz * nt * sizeof(double));
       ((datasets_t *) opdata)->obj[nset].array = array;
       array = array + offset;
 
@@ -1007,7 +1007,7 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
 
       std::vector<bool> mask(gridsize * nt, false);
 
-      nmiss = 0;
+      numMissVals = 0;
 
       auto mm = varray_min_max(gridsize * nt, array);
 
@@ -1041,7 +1041,7 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
               }
             else
               {
-                nmiss++;
+                numMissVals++;
                 mask[i] = true;
               }
         }
@@ -1058,7 +1058,7 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
       if (Options::cdoVerbose)
         cdo_print("Dataset %s: dtype = %d  minval = %g  maxval = %g  missval = %g", varname, dtype, minval, maxval, missval);
 
-      if (nmiss)
+      if (numMissVals)
         {
           if (!(missval < minval || missval > maxval))
             {
@@ -1256,11 +1256,22 @@ dsets_init(datasets_t *dsets)
     }
 }
 #endif
-class ModuleImportcmsaf
+class Importcmsaf : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Importcmsaf",
+    .operators = { { "import_cmsaf", ImportcmsafHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Importcmsaf> registration = RegisterEntry<Importcmsaf>(module);
 #ifdef HAVE_LIBHDF5
   int gridID = -1, zaxisID;
-  size_t nmiss;
+  size_t numMissVals;
   int ivar;
   int varID, levelID, tsID;
   hid_t file_id;
@@ -1273,9 +1284,8 @@ class ModuleImportcmsaf
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -1423,14 +1433,14 @@ public:
                 double *array = dsets.obj[ivar].array + offset;
 
                 auto mm = varray_min_max_mv(gridsize, array, missval);
-                nmiss = gridsize - mm.n;
+                numMissVals = gridsize - mm.n;
 
                 if (Options::cdoVerbose)
-                  cdo_print(" Write var %d,  level %d, nmiss %zu, missval %g, minval %g, maxval %g", varID, levelID, nmiss, missval,
+                  cdo_print(" Write var %d,  level %d, numMissVals %zu, missval %g, minval %g, maxval %g", varID, levelID, numMissVals, missval,
                             mm.min, mm.max);
 
                 cdo_def_record(streamID, varID, levelID);
-                cdo_write_record(streamID, array, nmiss);
+                cdo_write_record(streamID, array, numMissVals);
               }
           }
       }
@@ -1455,19 +1465,8 @@ public:
     zaxisDestroy(zaxisID);
     taxisDestroy(taxisID);
 
-    for (ivar = 0; ivar < dsets.numSets; ++ivar) Free(dsets.obj[ivar].array);
+    for (ivar = 0; ivar < dsets.numSets; ++ivar) free(dsets.obj[ivar].array);
 
 #endif
-    cdo_finish();
   }
 };
-
-void *
-Importcmsaf(void *process)
-{
-  ModuleImportcmsaf importcmsaf;
-  importcmsaf.init(process);
-  importcmsaf.run();
-  importcmsaf.close();
-  return nullptr;
-}
diff --git a/src/Importfv3grid.cc b/src/Importfv3grid.cc
index 433ed06c414ad1dc629eb15feb541967fe8fe3ea..42b4783eb5a848c29eb1ffb611af1ed9b1922165 100644
--- a/src/Importfv3grid.cc
+++ b/src/Importfv3grid.cc
@@ -9,8 +9,19 @@
 
 #include "process_int.h"
 
-class ModuleImportfv3grid
+class Importfv3grid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Importfv3grid",
+    .operators = { { "import_fv3grid"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Importfv3grid> registration = RegisterEntry<Importfv3grid>(module);
 
   size_t gridsize1;
   size_t gridsize2;
@@ -28,9 +39,8 @@ class ModuleImportfv3grid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -75,13 +85,13 @@ public:
   {
     Varray<double> buffer(gridsize1);
     int varID, levelID;
-    size_t nmiss;
+    size_t numMissVals;
 
     {
       Varray<double> grid_corner(4 * gridsize2);
 
       cdo_inq_record(streamID1, &varID, &levelID);  // grid_lon
-      cdo_read_record(streamID1, buffer.data(), &nmiss);
+      cdo_read_record(streamID1, buffer.data(), &numMissVals);
 
       for (size_t j = 1; j < ny; ++j)
         for (size_t i = 1; i < nx; ++i)
@@ -95,7 +105,7 @@ public:
       gridDefXbounds(gridIDo, grid_corner.data());
 
       cdo_inq_record(streamID1, &varID, &levelID);  // grid_lat
-      cdo_read_record(streamID1, buffer.data(), &nmiss);
+      cdo_read_record(streamID1, buffer.data(), &numMissVals);
 
       for (size_t j = 1; j < ny; ++j)
         for (size_t i = 1; i < nx; ++i)
@@ -110,15 +120,15 @@ public:
     }
 
     cdo_inq_record(streamID1, &varID, &levelID);  // grid_lont
-    cdo_read_record(streamID1, buffer.data(), &nmiss);
+    cdo_read_record(streamID1, buffer.data(), &numMissVals);
     gridDefXvals(gridIDo, buffer.data());
 
     cdo_inq_record(streamID1, &varID, &levelID);  // grid_latt
-    cdo_read_record(streamID1, buffer.data(), &nmiss);
+    cdo_read_record(streamID1, buffer.data(), &numMissVals);
     gridDefYvals(gridIDo, buffer.data());
 
     cdo_inq_record(streamID1, &varID, &levelID);  // area
-    cdo_read_record(streamID1, buffer.data(), &nmiss);
+    cdo_read_record(streamID1, buffer.data(), &numMissVals);
 
     double sfclevel = 0;
     surfaceID = zaxisCreate(ZAXIS_SURFACE, 1);
@@ -151,19 +161,5 @@ public:
     vlistDestroy(vlistID2);
     gridDestroy(gridIDo);
     zaxisDestroy(surfaceID);
-
-    cdo_finish();
   }
 };
-
-void *
-Importfv3grid(void *process)
-{
-  ModuleImportfv3grid importfv3grid;
-
-  importfv3grid.init(process);
-  importfv3grid.run();
-  importfv3grid.close();
-
-  return nullptr;
-}
diff --git a/src/Importobs.cc b/src/Importobs.cc
index 92016f8858278c2b676250b34d06c8de966d63d1..5943c9b2f9cd9ebea72051d3b0ac6e7c6e266c64 100644
--- a/src/Importobs.cc
+++ b/src/Importobs.cc
@@ -5,12 +5,13 @@
 
 */
 
+#include <fstream>
+
 #include <cdi.h>
 
 #include "cdo_options.h"
 #include "process_int.h"
 #include "varray.h"
-#include "readline.h"
 #include <mpim_grid.h>
 #include "griddes.h"
 
@@ -49,9 +50,9 @@ write_data(CdoStreamID streamID, int vlistID, int nvars, Varray2D<double> &data)
     {
       auto gridsize = gridInqSize(vlistInqVarGrid(vlistID, varID));
       auto missval = vlistInqVarMissval(vlistID, varID);
-      auto nmiss = varray_num_mv(gridsize, data[varID], missval);
+      auto numMissVals = varray_num_mv(gridsize, data[varID], missval);
       cdo_def_record(streamID, varID, 0);
-      cdo_write_record(streamID, data[varID].data(), nmiss);
+      cdo_write_record(streamID, data[varID].data(), numMissVals);
     }
 }
 
@@ -63,11 +64,20 @@ get_date(const char *name)
   return date;
 }
 
-#define MAX_LINE_LEN 4096
-
-class ModuleImportobs
+class Importobs : public Process
 {
-  char line[MAX_LINE_LEN];
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Importobs",
+    .operators = { { "import_obs", 0, 0, "grid description file or name"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Importobs>(module);
+
   int nvars = 6;
   int vtime = 0;
   char dummy[32], station[32], datetime[32];
@@ -95,13 +105,8 @@ class ModuleImportobs
 
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    cdo_operator_add("import_obs", 0, 0, "grid description file or name");
-
     auto operatorID = cdo_operator_id();
 
     operator_input_arg(cdo_operator_enter(operatorID));
@@ -123,13 +128,6 @@ public:
     cdo_grid_to_degree(gridID, CDI_XAXIS, xvals, "grid center lon");
     cdo_grid_to_degree(gridID, CDI_YAXIS, yvals, "grid center lat");
 
-    fp = std::fopen(cdo_get_stream_name(0), "r");
-    if (fp == nullptr)
-      {
-        perror(cdo_get_stream_name(0));
-        exit(EXIT_FAILURE);
-      }
-
     vdate = get_date(cdo_get_stream_name(0));
     if (vdate <= 999999) vdate = vdate * 100 + 1;
 
@@ -152,13 +150,17 @@ public:
   void
   run()
   {
+    std::ifstream file(cdo_get_stream_name(0));
+    if (!file.is_open()) cdo_abort("Open failed on: %s\n", cdo_get_stream_name(0));
+
     int vdate0 = 0;
     int vtime0 = 0;
     // ntime = 0;
     int tsID = 0;
-    while (cdo::readline(fp, line, MAX_LINE_LEN))
+    std::string line;
+    while (std::getline(file, line))
       {
-        std::sscanf(line, "%s %s %s %g %g %g %d %g %g %g", dummy, station, datetime, &lat, &lon, &height1, &code, &pressure,
+        std::sscanf(line.c_str(), "%s %s %s %g %g %g %d %g %g %g", dummy, station, datetime, &lat, &lon, &height1, &code, &pressure,
                     &height2, &value);
         long vdate_l;
         std::sscanf(datetime, "%ld_%d", &vdate_l, &vtime);
@@ -221,6 +223,8 @@ public:
         */
       }
 
+    file.close();
+
     write_data(streamID, vlistID, nvars, data);
 
     if (Options::cdoVerbose) printf("lonmin=%g, lonmax=%g, latmin=%g, latmax=%g\n", lonmin, lonmax, latmin, latmax);
@@ -233,24 +237,9 @@ public:
   {
     cdo_stream_close(streamID);
 
-    std::fclose(fp);
-
     vlistDestroy(vlistID);
     gridDestroy(gridID);
     zaxisDestroy(zaxisID);
     taxisDestroy(taxisID);
-
-    cdo_finish();
   }
 };
-
-void *
-Importobs(void *process)
-{
-  ModuleImportobs importobs;
-  importobs.init(process);
-  importobs.run();
-  importobs.close();
-
-  return nullptr;
-}
diff --git a/src/Info.cc b/src/Info.cc
index a0db061e2a44f18ae2a2c81b8771700c1f1eb913..57241d7dfcd8e28fc9d8ed41217ccb3854424654 100644
--- a/src/Info.cc
+++ b/src/Info.cc
@@ -32,7 +32,7 @@ struct Infostat
   double sum = 0.0;
   double sumi = 0.0;
   size_t nvals = 0;
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   int nlevels = 0;
 };
 
@@ -169,7 +169,7 @@ print_map(int nlon, int nlat, const Varray<T> &varray, double missval, double mi
           TextColor color(BLACK);
           switch (c)
             {
-            // clang-format off
+              // clang-format off
             case '0': mode = BRIGHT  ; color = BLUE   ; break;
             case '1': mode = MODELESS; color = BLUE   ; break;
             case '2': mode = BRIGHT  ; color = CYAN   ; break;
@@ -258,7 +258,7 @@ static void
 infostat_init(Infostat &infostat)
 {
   infostat.nvals = 0;
-  infostat.nmiss = 0;
+  infostat.numMissVals = 0;
   infostat.nlevels = 0;
   infostat.min = DBL_MAX;
   infostat.max = -DBL_MAX;
@@ -282,7 +282,7 @@ print_header(int fileIndex, bool lvinfo, const std::string &e, const std::string
 static void
 compute_stat_real(const Field &field, Infostat &infostat, size_t &imiss, size_t gridsize)
 {
-  if (infostat.nmiss)
+  if (infostat.numMissVals)
     {
       auto nvals = field_min_max_sum_mv(field, infostat.min, infostat.max, infostat.sum);
       imiss = gridsize - nvals;
@@ -329,7 +329,7 @@ print_stat_comp(const Infostat &infostat)
   fprintf(stdout, "   -  (%#12.5g,%#12.5g)  -", arrmean_r, arrmean_i);
 }
 
-class ModuleInfo
+class Info : public Process
 {
   enum
   {
@@ -337,6 +337,26 @@ class ModuleInfo
     E_CODE,
     E_PARAM
   };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Info",
+    .operators = { { "info", E_PARAM, 0, InfoHelp },
+                   { "infop", E_PARAM, 0, InfoHelp },
+                   { "infon", E_NAME, 0, InfoHelp },
+                   { "infoc", E_CODE, 0, InfoHelp },
+                   { "vinfon", E_NAME, 0, InfoHelp },
+                   { "xinfon", E_NAME, 0, InfoHelp },
+                   { "map", E_PARAM, 0, InfoHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { -1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Info> registration = RegisterEntry<Info>(module);
+
+  int VINFON, XINFON, MAP;
   size_t imiss = 0;
   char paramstr[32];
 
@@ -348,24 +368,14 @@ class ModuleInfo
 
   std::string e;  // parameter_name
   std::string v;  // verbose;
-                  //
-  int VINFON, XINFON, MAP;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-            cdo_operator_add("info",   E_PARAM,  0, nullptr);
-            cdo_operator_add("infop",  E_PARAM,  0, nullptr);
-            cdo_operator_add("infon",  E_NAME,   0, nullptr);
-            cdo_operator_add("infoc",  E_CODE,   0, nullptr);
-    VINFON = cdo_operator_add("vinfon", E_NAME,   0, nullptr);
-    XINFON = cdo_operator_add("xinfon", E_NAME,   0, nullptr);
-    MAP    = cdo_operator_add("map",    E_PARAM,  0, nullptr);
-    // clang-format on
+    VINFON = module.get_id("vinfon");
+    XINFON = module.get_id("xinfon");
+    MAP = module.get_id("map");
 
     operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -422,19 +432,17 @@ public:
                 const auto &var = varList[varID];
                 field.init(var);
                 cdo_read_record(streamID, field);
-                auto nmiss = field.nmiss;
+                auto numMissVals = field.numMissVals;
 
                 indg = lvinfo ? varID + 1 : indg + 1;
 
-                auto gridsize = var.gridsize;
-
                 auto loutput = !lvinfo;
 
                 if (loutput) infostat_init(infostat[varID]);
 
                 auto &infostatr = infostat[varID];
                 infostatr.nlevels += 1;
-                infostatr.nmiss += nmiss;
+                infostatr.numMissVals += numMissVals;
 
                 if (var.nlevels == infostatr.nlevels) loutput = true;
 
@@ -455,7 +463,7 @@ public:
                     else
                       fprintf(stdout, "%7g ", cdo_zaxis_inq_level(var.zaxisID, levelID));
 
-                    fprintf(stdout, "%8zu %7zu ", gridsize, infostatr.nmiss);
+                    fprintf(stdout, "%8zu %7zu ", var.gridsize, infostatr.numMissVals);
                     reset_text_color(stdout);
 
                     fprintf(stdout, ":");
@@ -464,15 +472,15 @@ public:
                   }
 
                 // clang-format off
-              if (var.nwpv == CDI_REAL) compute_stat_real(field, infostatr, imiss, gridsize);
-              else                      compute_stat_comp(field, infostatr, imiss, gridsize);
+                if (var.nwpv == CDI_REAL) compute_stat_real(field, infostatr, imiss, var.gridsize);
+                else                      compute_stat_comp(field, infostatr, imiss, var.gridsize);
                 // clang-format on
 
                 if (loutput)
                   {
                     // clang-format off
-                  if (var.nwpv == CDI_REAL) print_stat_real(infostatr);
-                  else                      print_stat_comp(infostatr);
+                    if (var.nwpv == CDI_REAL) print_stat_real(infostatr);
+                    else                      print_stat_comp(infostatr);
                     // clang-format on
 
                     reset_text_color(stdout);
@@ -481,16 +489,16 @@ public:
 
                     // set_text_color(stdout, GREEN);
                     // clang-format off
-                  if      (operfunc == E_NAME) fprintf(stdout, "%-14s", var.name.c_str());
-                  else if (operfunc == E_CODE) fprintf(stdout, "%4d   ", var.code);
-                  else                         fprintf(stdout, "%-14s", paramstr);
+                    if      (operfunc == E_NAME) fprintf(stdout, "%-14s", var.name.c_str());
+                    else if (operfunc == E_CODE) fprintf(stdout, "%4d   ", var.code);
+                    else                         fprintf(stdout, "%-14s", paramstr);
                     // clang-format on
                     // reset_text_color(stdout);
 
                     fprintf(stdout, "\n");
                   }
 
-                if (imiss != nmiss && nmiss) cdo_warning("Found %zu of %zu missing values!", imiss, nmiss);
+                if (imiss != numMissVals && numMissVals) cdo_warning("Found %zu of %zu missing values!", imiss, numMissVals);
 
                 if (operatorID == MAP)
                   {
@@ -519,17 +527,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Info(void *process)
-{
-  ModuleInfo info;
-  info.init(process);
-  info.run();
-  info.close();
-
-  return nullptr;
-}
diff --git a/src/Input.cc b/src/Input.cc
index 58e8a554fa20a5bb758762f7484ecaf92fe47b0d..1dd086fde53c11009db73263846dd7fe2aa4b8b4 100644
--- a/src/Input.cc
+++ b/src/Input.cc
@@ -38,8 +38,25 @@ input_iarray(size_t nval, int *array)
   return ival;
 }
 
-class ModuleInput
+class Input : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Input",
+    // clang-format off
+    .operators = { { "input", InputHelp },
+                   { "inputsrv", InputHelp },
+                   { "inputext", InputHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Input> registration = RegisterEntry<Input>(module);
+
+  int INPUT, INPUTSRV, INPUTEXT;
   int varID = 0;
   size_t gridsize0 = 0, gridsize = 0;
   int taxisID = 0;
@@ -58,18 +75,13 @@ class ModuleInput
   int gridID = -1;
   int zaxisID = -1;
 
-  int INPUT, INPUTSRV, INPUTEXT;
-
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    INPUT = cdo_operator_add("input", 0, 0, nullptr);
-    INPUTSRV = cdo_operator_add("inputsrv", 0, 0, nullptr);
-    INPUTEXT = cdo_operator_add("inputext", 0, 0, nullptr);
+    INPUT = module.get_id("input");
+    INPUTSRV = module.get_id("inputsrv");
+    INPUTEXT = module.get_id("inputext");
 
     operatorID = cdo_operator_id();
 
@@ -85,6 +97,7 @@ public:
 
     nlevs = (zaxisID == -1) ? 1 : zaxisInqSize(zaxisID);
   }
+
   void
   run()
   {
@@ -221,9 +234,9 @@ public:
         for (int levelID = 0; levelID < nlevs; ++levelID)
           {
             auto offset = gridsize * levelID;
-            auto nmiss = array_num_mv(gridsize, &array[offset], missval);
+            auto numMissVals = array_num_mv(gridsize, &array[offset], missval);
             cdo_def_record(streamID, varID, levelID);
-            cdo_write_record(streamID, &array[offset], nmiss);
+            cdo_write_record(streamID, &array[offset], numMissVals);
           }
 
         nrecs++;
@@ -239,17 +252,5 @@ public:
         cdo_stream_close(streamID);
         vlistDestroy(vlistID);
       }
-    cdo_finish();
   }
 };
-void *
-Input(void *process)
-{
-  ModuleInput input;
-
-  input.init(process);
-  input.run();
-  input.close();
-
-  return nullptr;
-}
diff --git a/src/Intgrid.cc b/src/Intgrid.cc
index 02b76049ea5787e6d02ffe1f21038c81d8cc54ee..9d50f086864e16f8f155e844508a8736c522df10 100644
--- a/src/Intgrid.cc
+++ b/src/Intgrid.cc
@@ -240,8 +240,25 @@ boxavg(const Field &field1, Field &field2, size_t xinc, size_t yinc)
   field_num_mv(field2);
 }
 
-class ModuleIntgrid
+class Intgrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Intgrid",
+    .operators = { { "intgridbil"},
+                   { "intgriddis"},
+                   { "intgridnn"},
+                   { "interpolate"},
+                   { "boxavg"},
+                   { "thinout"} },
+    .aliases = { { "intgrid", "intgridbil" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Intgrid> registration = RegisterEntry<Intgrid>(module);
+  int INTGRIDBIL, INTGRIDDIS, INTGRIDNN, INTERPOLATE, BOXAVG, THINOUT;
   int gridID2 = -1;
   int xinc = 1, yinc = 1;
 
@@ -258,20 +275,17 @@ class ModuleIntgrid
   VarList varList1, varList2;
   Field field1, field2;
 
-  int INTGRIDBIL, INTGRIDDIS, INTGRIDNN, INTERPOLATE, BOXAVG, THINOUT;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    INTGRIDBIL = cdo_operator_add("intgridbil", 0, 0, nullptr);
-    INTGRIDDIS = cdo_operator_add("intgriddis", 0, 0, nullptr);
-    INTGRIDNN = cdo_operator_add("intgridnn", 0, 0, nullptr);
-    INTERPOLATE = cdo_operator_add("interpolate", 0, 0, nullptr);
-    BOXAVG = cdo_operator_add("boxavg", 0, 0, nullptr);
-    THINOUT = cdo_operator_add("thinout", 0, 0, nullptr);
+    INTGRIDBIL = module.get_id("intgridbil");
+    INTGRIDDIS = module.get_id("intgriddis");
+    INTGRIDNN = module.get_id("intgridnn");
+    INTERPOLATE = module.get_id("interpolate");
+    BOXAVG = module.get_id("boxavg");
+    THINOUT = module.get_id("thinout");
 
     operatorID = cdo_operator_id();
 
@@ -309,8 +323,7 @@ public:
           {
             if (index == 0)
               {
-                if (gridtype != GRID_LONLAT && gridtype != GRID_GAUSSIAN)
-                  cdo_abort("%s data unsupported!", gridNamePtr(gridtype));
+                if (gridtype != GRID_LONLAT && gridtype != GRID_GAUSSIAN) cdo_abort("%s data unsupported!", gridNamePtr(gridtype));
 
                 gridID2 = gen_boxavg_grid(gridID1, xinc, yinc);
               }
@@ -384,13 +397,13 @@ public:
               }
             else
               {
-                cdo_read_record(streamID1, field1.vec_d.data(), &field1.nmiss);
+                cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
 
                 field1.grid = varList1[varID].gridID;
                 field1.missval = varList1[varID].missval;
                 field2.grid = gridID2;
                 field2.missval = field1.missval;
-                field2.nmiss = 0;
+                field2.numMissVals = 0;
               }
 
             // clang-format off
@@ -406,7 +419,7 @@ public:
             if (lfieldmem)
               cdo_write_record(streamID2, field2);
             else
-              cdo_write_record(streamID2, field2.vec_d.data(), field2.nmiss);
+              cdo_write_record(streamID2, field2.vec_d.data(), field2.numMissVals);
           }
         tsID++;
       }
@@ -417,19 +430,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Intgrid(void *process)
-{
-
-  ModuleIntgrid intgrid;
-  intgrid.init(process);
-  intgrid.run();
-  intgrid.close();
-
-  return nullptr;
-}
diff --git a/src/Intgridtraj.cc b/src/Intgridtraj.cc
index 50618e783bc7054001a6eb8e75c5c8c5c764d82f..1cddbb4cb95f0dba504ca562fa017b8555c43ed1 100644
--- a/src/Intgridtraj.cc
+++ b/src/Intgridtraj.cc
@@ -42,9 +42,20 @@ read_nextpos(FILE *fp, int calendar, JulianDate &julianDate, double &xpos, doubl
   return stat;
 }
 
-class ModuleIntgridtraj
+class Intgridtraj : public Process
 {
-  size_t nmiss = 0;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Intgridtraj",
+    .operators = { { "intgridtraj"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Intgridtraj> registration = RegisterEntry<Intgridtraj>(module);
+  size_t numMissVals = 0;
   double xpos, ypos;
   int calendar = CALENDAR_STANDARD;
 
@@ -71,11 +82,9 @@ class ModuleIntgridtraj
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     operator_input_arg("filename with grid trajectories");
     operator_check_argc(1);
 
@@ -146,8 +155,8 @@ public:
         const auto gridsize = varList1[varID].gridsize;
         const auto offset = gridsize * levelID;
         auto single1 = &vardata1[varID][offset];
-        cdo_read_record(streamID1, single1, &nmiss);
-        if (nmiss) cdo_abort("Missing values unsupported for this operator!");
+        cdo_read_record(streamID1, single1, &numMissVals);
+        if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
       }
 
     int tsIDo = 0;
@@ -167,8 +176,8 @@ public:
             const auto gridsize = varList1[varID].gridsize;
             const auto offset = gridsize * levelID;
             auto single2 = &vardata2[varID][offset];
-            cdo_read_record(streamID1, single2, &nmiss);
-            if (nmiss) cdo_abort("Missing values unsupported for this operator!");
+            cdo_read_record(streamID1, single2, &numMissVals);
+            if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
           }
 
         while (julianDate_to_seconds(julianDate) < julianDate_to_seconds(julianDate2))
@@ -203,14 +212,14 @@ public:
 
                     field1.grid = varList1[varID].gridID;
                     field1.missval = varList1[varID].missval;
-                    field1.nmiss = nmiss;
+                    field1.numMissVals = numMissVals;
                     field2.grid = gridID2;
-                    field2.nmiss = 0;
+                    field2.numMissVals = 0;
 
                     intgridbil(field1, field2);
 
                     cdo_def_record(streamID2, varID, levelID);
-                    cdo_write_record(streamID2, field2.vec_d.data(), field2.nmiss);
+                    cdo_write_record(streamID2, field2.vec_d.data(), field2.numMissVals);
                   }
               }
 
@@ -240,19 +249,5 @@ public:
     std::fclose(fp);
     if (streamID2 != CDO_STREAM_UNDEF) cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Intgridtraj(void *process)
-{
-  ModuleIntgridtraj intgridtraj;
-
-  intgridtraj.init(process);
-  intgridtraj.run();
-  intgridtraj.close();
-
-  return nullptr;
-}
diff --git a/src/Intlevel.cc b/src/Intlevel.cc
index b0ccbf6fcc1833fd6394f972873359391e21d9d3..d5e87c999a67cca7d1fe4b9c50d9cad862baf02f 100644
--- a/src/Intlevel.cc
+++ b/src/Intlevel.cc
@@ -310,7 +310,7 @@ intlevel_get_parameter(std::string &zdescription, std::string &zvarname)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       auto def_level = false;
@@ -326,24 +326,24 @@ intlevel_get_parameter(std::string &zdescription, std::string &zvarname)
           if (nvalues == 1 && value.empty()) nvalues = 0;
 
           // clang-format off
-         if (key == "level")
+          if (key == "level")
             {
               if (def_zdescription) cdo_abort("Parameter level and zdescription can't be mixed!");
               levels.resize(nvalues);
               for (int i = 0; i < nvalues; ++i) levels[i] = parameter_to_double(values[i]);
               def_level = true;
             }
-         else if (key == "zdescription")
+          else if (key == "zdescription")
             {
-              if (def_level) cdo_abort("Parameter file and level can't be mixed!");
+              if (def_level) cdo_abort("Parameter zdescription and level can't be mixed!");
               zdescription = value;
               def_zdescription = true;
             }
-         else if (key == "zvarname")
+          else if (key == "zvarname")
             {
               zvarname = value;
             }
-         else cdo_abort("Invalid parameter key >%s<!", key);
+          else cdo_abort("Invalid parameter key >%s<!", key);
           // clang-format on
         }
     }
@@ -426,8 +426,24 @@ handle_empty_zvar(size_t &wisize, int &nlevel, std::vector<double> &lev1, const
   wisize = nlev2;
 }
 
-class ModuleIntlevel
+class Intlevel : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Intlevel",
+    // clang-format off
+    .operators = { { "intlevel", IntlevelHelp },
+                   { "intlevelx", IntlevelHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Intlevel> registration = RegisterEntry<Intlevel>(module);
+
+  int INTLEVEL, INTLEVELX;
   int zaxisID1 = -1;
   int nlevel = 0;
 
@@ -439,7 +455,7 @@ class ModuleIntlevel
 
   std::vector<bool> processVars;
   std::vector<bool> interpVars;
-  std::vector<std::vector<size_t>> varnmiss;
+  std::vector<std::vector<size_t>> varnumMissVals;
   Field3DVector vardata1;
   Field3DVector vardata2;
 
@@ -468,17 +484,12 @@ class ModuleIntlevel
   VarList varList1;
   VarList varList2;
 
-  int INTLEVEL, INTLEVELX;
-
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    INTLEVEL = cdo_operator_add("intlevel", 0, 0, nullptr);
-    INTLEVELX = cdo_operator_add("intlevelx", 0, 0, nullptr);
+    INTLEVEL = module.get_id("intlevel");
+    INTLEVELX = module.get_id("intlevelx");
 
     (void) (INTLEVEL);  // unused
 
@@ -510,7 +521,7 @@ public:
           }
       }
 
-     nlev2 = lev2.size();
+    nlev2 = lev2.size();
 
     if (Options::cdoVerbose)
       for (int i = 0; i < nlev2; ++i) cdo_print("level out %d: %g", i, lev2[i]);
@@ -559,11 +570,11 @@ public:
 
     processVars = std::vector<bool>(nvars);
     interpVars = std::vector<bool>(nvars, false);
-    varnmiss = std::vector<std::vector<size_t>>(nvars);
+    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
     vardata1 = Field3DVector(nvars);
     vardata2 = Field3DVector(nvars);
 
-    const int maxlev = (nlev1 > nlev2) ? nlev1 : nlev2;
+    int maxlev = (nlev1 > nlev2) ? nlev1 : nlev2;
 
     for (int varID = 0; varID < nvars; ++varID)
       {
@@ -575,12 +586,13 @@ public:
 
         if (interpVars[varID])
           {
-            varnmiss[varID].resize(maxlev, 0);
+            varnumMissVals[varID].resize(maxlev, 0);
             vardata2[varID].init(varList2[varID]);
           }
-        else { varnmiss[varID].resize(nlevels); }
+        else { varnumMissVals[varID].resize(nlevels); }
       }
   }
+
   void
   run()
   {
@@ -599,7 +611,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+            cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
@@ -627,9 +639,9 @@ public:
                   {
                     auto offset = gridsize * levelID;
                     if (memType == MemType::Float)
-                      varnmiss[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_f[offset], (float) missval);
+                      varnumMissVals[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_f[offset], (float) missval);
                     else
-                      varnmiss[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_d[offset], missval);
+                      varnumMissVals[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_d[offset], missval);
                   }
               }
           }
@@ -642,7 +654,7 @@ public:
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
-                                     varnmiss[varID][levelID]);
+                                     varnumMissVals[varID][levelID]);
                   }
               }
           }
@@ -650,24 +662,11 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Intlevel(void *process)
-{
-  ModuleIntlevel intlevel;
-
-  intlevel.init(process);
-  intlevel.run();
-  intlevel.close();
-
-  return nullptr;
-}
diff --git a/src/Intlevel3d.cc b/src/Intlevel3d.cc
index cbb551461a8bccd21fa015ac125577f29d146771..e4225db4ad2f3860a67f8dce389079201b59a6d1 100644
--- a/src/Intlevel3d.cc
+++ b/src/Intlevel3d.cc
@@ -105,9 +105,9 @@ read_source_coordinate(int streamNumber, VarList &varList2, Varray<float> &zleve
       int varID, levelID;
       cdo_inq_record(streamID2, &varID, &levelID);
       auto offset = gridsize * levelID;
-      size_t nmiss;
-      cdo_read_record_f(streamID2, &zlevelsIn[offset], &nmiss);
-      if (0 != nmiss) cdo_abort("Input vertical coordinate variables are not allowed to contain missing values.");
+      size_t numMissVals;
+      cdo_read_record_f(streamID2, &zlevelsIn[offset], &numMissVals);
+      if (0 != numMissVals) cdo_abort("Input vertical coordinate variables are not allowed to contain missing values.");
     }
 
   cdo_stream_close(streamID2);
@@ -136,16 +136,28 @@ read_target_coordinate(const std::string &fileName, VarList &varList0, Varray<fl
       int varID, levelID;
       streamInqRecord(streamID0, &varID, &levelID);
       auto offset = gridsize * levelID;
-      size_t nmiss;
-      streamReadRecordF(streamID0, &zlevelsOut[offset], &nmiss);
-      if (0 != nmiss) cdo_abort("Output vertical coordinate variables are not allowd to contain missing values.");
+      size_t numMissVals;
+      streamReadRecordF(streamID0, &zlevelsOut[offset], &numMissVals);
+      if (0 != numMissVals) cdo_abort("Output vertical coordinate variables are not allowd to contain missing values.");
     }
 
   streamClose(streamID0);
 }
 
-class ModuleIntlevel3d
+class Intlevel3d : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Intlevel3d",
+    .operators = { { "intlevel3d", Intlevel3dHelp }, { "intlevelx3d", Intlevel3dHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Intlevel3d> registration = RegisterEntry<Intlevel3d>(module);
+  int INTLEVEL3D, INTLEVELX3D;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -170,7 +182,7 @@ class ModuleIntlevel3d
   std::vector<bool> processVars;
   std::vector<bool> interpVars;
 
-  std::vector<std::vector<size_t>> varnmiss;
+  std::vector<std::vector<size_t>> varnumMissVals;
 
   Field3DVector vardata1;
   Field3DVector vardata2;
@@ -180,17 +192,14 @@ class ModuleIntlevel3d
 
   Varray<float> zlevelsOut;
 
-  int INTLEVEL3D, INTLEVELX3D;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-   INTLEVEL3D  = cdo_operator_add("intlevel3d",  0, 0, nullptr);
-   INTLEVELX3D = cdo_operator_add("intlevelx3d", 0, 0, nullptr);
+INTLEVEL3D = module.get_id("intlevel3d");
+INTLEVELX3D = module.get_id("intlevelx3d");
     // clang-format on
 
     (void) INTLEVEL3D;  // unused
@@ -294,7 +303,7 @@ public:
 
     processVars = std::vector<bool>(nvars);
     interpVars = std::vector<bool>(nvars, false);        // marker for variables to be interpolated
-    varnmiss = std::vector<std::vector<size_t>>(nvars);  // can for missing values of arbitrary variables
+    varnumMissVals = std::vector<std::vector<size_t>>(nvars);  // can for missing values of arbitrary variables
     vardata1 = Field3DVector(nvars);
     vardata2 = Field3DVector(nvars);
 
@@ -314,12 +323,12 @@ public:
         interpVars[varID] = (zaxisID == zaxisID1 && varID != oz3dvarID && gridsize == gridSize && gridsizeo == gridsize);
         if (interpVars[varID])
           {
-            varnmiss[varID].resize(maxlev, 0);
+            varnumMissVals[varID].resize(maxlev, 0);
             vardata2[varID].init(varList3[varID]);
           }
         else
           {
-            varnmiss[varID].resize(nlevel);
+            varnumMissVals[varID].resize(nlevel);
             if (Options::cdoVerbose)
               cdo_print("Ignore variable %s (levels=%d gridsize=%zu)!", varList1[varID].name, nlevel, gridsize);
           }
@@ -352,7 +361,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+            cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
@@ -370,9 +379,9 @@ public:
                   {
                     auto offset = gridsize * levelID;
                     if (memType == MemType::Float)
-                      varnmiss[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_f[offset], (float) missval);
+                      varnumMissVals[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_f[offset], (float) missval);
                     else
-                      varnmiss[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_d[offset], missval);
+                      varnumMissVals[varID][levelID] = array_num_mv(gridsize, &vardata2[varID].vec_d[offset], missval);
                   }
               }
             else
@@ -390,7 +399,7 @@ public:
                   {
                     cdo_def_record(streamID3, varID, levelID);
                     cdo_write_record(streamID3, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
-                                     varnmiss[varID][levelID]);
+                                     varnumMissVals[varID][levelID]);
                   }
               }
           }
@@ -413,19 +422,5 @@ public:
     cdo_stream_close(streamID3);
 
     vlistDestroy(vlistID3);
-
-    cdo_finish();
   }
 };
-
-void *
-Intlevel3d(void *process)
-{
-  ModuleIntlevel3d intlevel3d;
-
-  intlevel3d.init(process);
-  intlevel3d.run();
-  intlevel3d.close();
-
-  return nullptr;
-}
diff --git a/src/Intntime.cc b/src/Intntime.cc
index d83f6e49a2cf7935de5efb0d020d6770c9b83f55..69907f3dac5abbfe49504c09b25a3b8aa295c763 100644
--- a/src/Intntime.cc
+++ b/src/Intntime.cc
@@ -23,8 +23,19 @@
 
 void interp_time(double fac1, double fac2, const double *array1, const double *array2, Field &field3, bool withMissval);
 
-class ModuleIntntime
+class Intntime : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Intntime",
+    .operators = { { "intntime", InttimeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Intntime> registration = RegisterEntry<Intntime>(module);
   int curFirst = 0, curSecond = 1;
 
   CdoStreamID streamID1;
@@ -46,14 +57,13 @@ class ModuleIntntime
   VarList varList;
   Field field3;
   std::vector<RecordInfo> recList;
-  Varray3D<size_t> nmiss;
+  Varray3D<size_t> numMissVals;
   Varray3D<double> vardata;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("number of timesteps between 2 timesteps");
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
@@ -73,9 +83,9 @@ public:
     auto maxrecs = vlistNrecs(vlistID1);
     recList = std::vector<RecordInfo>(maxrecs);
 
-    nmiss = Varray3D<size_t>(2);
-    nmiss[0].resize(nvars);
-    nmiss[1].resize(nvars);
+    numMissVals = Varray3D<size_t>(2);
+    numMissVals[0].resize(nvars);
+    numMissVals[1].resize(nvars);
     vardata = Varray3D<double>(2);
     vardata[0].resize(nvars);
     vardata[1].resize(nvars);
@@ -84,8 +94,8 @@ public:
       {
         auto gridsize = varList[varID].gridsize;
         auto nlevel = varList[varID].nlevels;
-        nmiss[0][varID].resize(nlevel);
-        nmiss[1][varID].resize(nlevel);
+        numMissVals[0][varID].resize(nlevel);
+        numMissVals[1][varID].resize(nlevel);
         vardata[0][varID].resize(gridsize * nlevel);
         vardata[1][varID].resize(gridsize * nlevel);
       }
@@ -114,10 +124,10 @@ public:
         cdo_inq_record(streamID1, &varID, &levelID);
         auto offset = varList[varID].gridsize * levelID;
         auto single1 = &vardata[curFirst][varID][offset];
-        cdo_read_record(streamID1, single1, &nmiss[curFirst][varID][levelID]);
+        cdo_read_record(streamID1, single1, &numMissVals[curFirst][varID][levelID]);
 
         cdo_def_record(streamID2, varID, levelID);
-        cdo_write_record(streamID2, single1, nmiss[curFirst][varID][levelID]);
+        cdo_write_record(streamID2, single1, numMissVals[curFirst][varID][levelID]);
       }
   }
   void
@@ -140,7 +150,7 @@ public:
 
             auto offset = varList[varID].gridsize * levelID;
             auto single2 = &vardata[curSecond][varID][offset];
-            cdo_read_record(streamID1, single2, &nmiss[curSecond][varID][levelID]);
+            cdo_read_record(streamID1, single2, &numMissVals[curSecond][varID][levelID]);
           }
 
         for (int it = 1; it < numts; it++)
@@ -168,7 +178,7 @@ public:
 
                 field3.init(varList[varID]);
 
-                auto withMissval = (nmiss[curFirst][varID][levelID] || nmiss[curSecond][varID][levelID]);
+                auto withMissval = (numMissVals[curFirst][varID][levelID] || numMissVals[curSecond][varID][levelID]);
                 interp_time(fac1, fac2, single1, single2, field3, withMissval);
 
                 cdo_def_record(streamID2, varID, levelID);
@@ -186,7 +196,7 @@ public:
             auto single2 = &vardata[curSecond][varID][offset];
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, single2, nmiss[curSecond][varID][levelID]);
+            cdo_write_record(streamID2, single2, numMissVals[curSecond][varID][levelID]);
           }
 
         julianDate1 = julianDate2;
@@ -198,17 +208,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Intntime(void *process)
-{
-  ModuleIntntime intntime;
-  intntime.init(process);
-  intntime.run();
-  intntime.close();
-  return nullptr;
-}
diff --git a/src/Inttime.cc b/src/Inttime.cc
index dfd853e0ec1d4ec4baa51eec4217ee23f0d63f23..518ed2f9d904ae7d892a603350b3afa628a2ab34 100644
--- a/src/Inttime.cc
+++ b/src/Inttime.cc
@@ -27,7 +27,7 @@ size_t
 interp_time(double fac1, double fac2, size_t gridsize, const double *v1, const double *v2, Varray<T> &v3, bool withMissval,
             double missval)
 {
-  size_t nmiss3 = 0;
+  size_t numMissVals3 = 0;
 
   if (withMissval)
     {
@@ -42,7 +42,7 @@ interp_time(double fac1, double fac2, size_t gridsize, const double *v1, const d
           else
             {
               v3[i] = missval;
-              nmiss3++;
+              numMissVals3++;
             }
         }
     }
@@ -54,16 +54,16 @@ interp_time(double fac1, double fac2, size_t gridsize, const double *v1, const d
       for (size_t i = 0; i < gridsize; ++i) v3[i] = v1[i] * fac1 + v2[i] * fac2;
     }
 
-  return nmiss3;
+  return numMissVals3;
 }
 
 void
 interp_time(double fac1, double fac2, const double *array1, const double *array2, Field &field3, bool withMissval)
 {
   if (field3.memType == MemType::Float)
-    field3.nmiss = interp_time(fac1, fac2, field3.gridsize, array1, array2, field3.vec_f, withMissval, field3.missval);
+    field3.numMissVals = interp_time(fac1, fac2, field3.gridsize, array1, array2, field3.vec_f, withMissval, field3.missval);
   else
-    field3.nmiss = interp_time(fac1, fac2, field3.gridsize, array1, array2, field3.vec_d, withMissval, field3.missval);
+    field3.numMissVals = interp_time(fac1, fac2, field3.gridsize, array1, array2, field3.vec_d, withMissval, field3.missval);
 }
 
 static void
@@ -95,8 +95,19 @@ adjust_time_units(int taxisID, int timeUnitsOut)
     }
 }
 
-class ModuleInttime
+class Inttime : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Inttime",
+    .operators = { { "inttime", InttimeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Inttime> registration = RegisterEntry<Inttime>(module);
   CdiDateTime sDateTime{};
   int incrPeriod = 0, incrUnits = 3600, timeUnits = TUNIT_HOUR;
 
@@ -124,15 +135,14 @@ class ModuleInttime
   std::vector<RecordInfo> recList;
   Field field3;
 
-  Varray3D<size_t> nmiss;
+  Varray3D<size_t> numMissVals;
   Varray3D<double> vardata;
   VarList varList;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("date,time<,increment> (format YYYY-MM-DD,hh:mm:ss)");
     if (cdo_operator_argc() < 2) cdo_abort("Too few arguments!");
@@ -158,9 +168,9 @@ public:
     auto maxrecs = vlistNrecs(vlistID1);
     recList = std::vector<RecordInfo>(maxrecs);
 
-    nmiss = Varray3D<size_t>(2);
-    nmiss[0].resize(nvars);
-    nmiss[1].resize(nvars);
+    numMissVals = Varray3D<size_t>(2);
+    numMissVals[0].resize(nvars);
+    numMissVals[1].resize(nvars);
     vardata = Varray3D<double>(2);
     vardata[0].resize(nvars);
     vardata[1].resize(nvars);
@@ -169,8 +179,8 @@ public:
       {
         auto gridsize = varList[varID].gridsize;
         auto nlevel = varList[varID].nlevels;
-        nmiss[0][varID].resize(nlevel);
-        nmiss[1][varID].resize(nlevel);
+        numMissVals[0][varID].resize(nlevel);
+        numMissVals[1][varID].resize(nlevel);
         vardata[0][varID].resize(gridsize * nlevel);
         vardata[1][varID].resize(gridsize * nlevel);
       }
@@ -201,7 +211,7 @@ public:
         cdo_inq_record(streamID1, &varID, &levelID);
         auto offset = varList[varID].gridsize * levelID;
         auto single1 = &vardata[curFirst][varID][offset];
-        cdo_read_record(streamID1, single1, &nmiss[curFirst][varID][levelID]);
+        cdo_read_record(streamID1, single1, &numMissVals[curFirst][varID][levelID]);
       }
 
     if (Options::cdoVerbose)
@@ -241,7 +251,7 @@ public:
 
             auto offset = varList[varID].gridsize * levelID;
             auto single2 = &vardata[curSecond][varID][offset];
-            cdo_read_record(streamID1, single2, &nmiss[curSecond][varID][levelID]);
+            cdo_read_record(streamID1, single2, &numMissVals[curSecond][varID][levelID]);
           }
 
         while (julianDate_to_seconds(julianDate) <= julianDate_to_seconds(julianDate2))
@@ -278,7 +288,7 @@ public:
 
                     field3.init(varList[varID]);
 
-                    auto withMissval = (nmiss[curFirst][varID][levelID] || nmiss[curSecond][varID][levelID]);
+                    auto withMissval = (numMissVals[curFirst][varID][levelID] || numMissVals[curSecond][varID][levelID]);
                     interp_time(fac1, fac2, single1, single2, field3, withMissval);
 
                     cdo_def_record(streamID2, varID, levelID);
@@ -302,17 +312,5 @@ public:
     cdo_stream_close(streamID1);
 
     if (tsIDo == 0) cdo_warning("Start date/time %s out of range, no time steps interpolated!", datetime_to_string(sDateTime));
-
-    cdo_finish();
   }
 };
-void *
-Inttime(void *process)
-{
-  ModuleInttime inttime;
-  inttime.init(process);
-  inttime.run();
-  inttime.close();
-
-  return nullptr;
-}
diff --git a/src/Intyear.cc b/src/Intyear.cc
index ced66b47d46387b3ace14b6e04d68992e4515f0e..33f4cbd303c00d2df34d870574d6ce2e3cdf5555 100644
--- a/src/Intyear.cc
+++ b/src/Intyear.cc
@@ -24,7 +24,7 @@ static size_t
 intlin_year(double fac1, double fac2, size_t gridsize, const Varray<double> &array1, const Varray<double> &array2,
             Varray<double> &array3, bool withMissval, double missval1, double missval2)
 {
-  size_t nmiss3 = 0;
+  size_t numMissVals3 = 0;
 
   if (withMissval)
     {
@@ -37,7 +37,7 @@ intlin_year(double fac1, double fac2, size_t gridsize, const Varray<double> &arr
           else
             {
               array3[i] = missval1;
-              nmiss3++;
+              numMissVals3++;
             }
         }
     }
@@ -46,11 +46,22 @@ intlin_year(double fac1, double fac2, size_t gridsize, const Varray<double> &arr
       for (size_t i = 0; i < gridsize; ++i) array3[i] = array1[i] * fac1 + array2[i] * fac2;
     }
 
-  return nmiss3;
+  return numMissVals3;
 }
 
-class ModuleIntyear
+class Intyear : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Intyear",
+    .operators = { { "intyear", IntyearHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, OBASE, NoRestriction },
+  };
+  inline static RegisterEntry<Intyear> registration = RegisterEntry<Intyear>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -71,9 +82,8 @@ class ModuleIntyear
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("years");
 
@@ -153,24 +163,24 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_inq_record(streamID2, &varID, &levelID);
 
-            size_t nmiss1, nmiss2;
-            cdo_read_record(streamID1, array1.data(), &nmiss1);
-            cdo_read_record(streamID2, array2.data(), &nmiss2);
+            size_t numMissVals1, numMissVals2;
+            cdo_read_record(streamID1, array1.data(), &numMissVals1);
+            cdo_read_record(streamID2, array2.data(), &numMissVals2);
 
             auto gridsize = varList1[varID].gridsize;
             auto missval1 = varList1[varID].missval;
             auto missval2 = varList2[varID].missval;
-            auto withMissval = (nmiss1 || nmiss2);
+            auto withMissval = (numMissVals1 || numMissVals2);
 
             for (int iy = 0; iy < nyears; iy++)
               {
                 auto fac1 = ((double) year2 - iyears[iy]) / (year2 - year1);
                 auto fac2 = ((double) iyears[iy] - year1) / (year2 - year1);
 
-                auto nmiss3 = intlin_year(fac1, fac2, gridsize, array1, array2, array3, withMissval, missval1, missval2);
+                auto numMissVals3 = intlin_year(fac1, fac2, gridsize, array1, array2, array3, withMissval, missval1, missval2);
 
                 cdo_def_record(streamIDs[iy], varID, levelID);
-                cdo_write_record(streamIDs[iy], array3.data(), nmiss3);
+                cdo_write_record(streamIDs[iy], array3.data(), numMissVals3);
               }
           }
 
@@ -185,18 +195,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Intyear(void *process)
-{
-  ModuleIntyear intyear;
-  intyear.init(process);
-  intyear.run();
-  intyear.close();
-
-  return nullptr;
-}
diff --git a/src/Invert.cc b/src/Invert.cc
index 1bb54933550a640173875548a8bf70bf1fceb5b5..5577958dd3b9b3cbb424d4c73a037a4ea42d7c29 100644
--- a/src/Invert.cc
+++ b/src/Invert.cc
@@ -258,7 +258,7 @@ invert_lat_des(int vlistID)
     }
 }
 
-class ModuleInvert
+class Invert : public Process
 {
   enum
   {
@@ -269,6 +269,23 @@ class ModuleInvert
     func_lat
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Invert",
+    .operators = { { "invertlat", func_all, func_lat, InvertHelp },
+                   { "invertlon", func_all, func_lon, InvertHelp },
+                   { "invertlatdes", func_hrd, func_lat, InvertHelp },
+                   { "invertlondes", func_hrd, func_lon, InvertHelp },
+                   { "invertlatdata", func_fld, func_lat, InvertHelp },
+                   { "invertlondata", func_fld, func_lon, InvertHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Invert> registration = RegisterEntry<Invert>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -283,17 +300,10 @@ class ModuleInvert
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  cdo_operator_add("invertlat",     func_all, func_lat, nullptr);
-  cdo_operator_add("invertlon",     func_all, func_lon, nullptr);
-  cdo_operator_add("invertlatdes",  func_hrd, func_lat, nullptr);
-  cdo_operator_add("invertlondes",  func_hrd, func_lon, nullptr);
-  cdo_operator_add("invertlatdata", func_fld, func_lat, nullptr);
-  cdo_operator_add("invertlondata", func_fld, func_lon, nullptr);
     // clang-format on
 
     const auto operatorID = cdo_operator_id();
@@ -368,18 +378,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Invert(void *process)
-{
-  ModuleInvert invert;
-
-  invert.init(process);
-  invert.run();
-  invert.close();
-
-  return nullptr;
-}
diff --git a/src/Invertlev.cc b/src/Invertlev.cc
index a0d6eb58b1f2d2404af646e35a231a094c7a5c94..9389f9dd6dba058584668a6ad16f1a6dad4c2c22 100644
--- a/src/Invertlev.cc
+++ b/src/Invertlev.cc
@@ -24,7 +24,6 @@ invertLevDes(int vlistID)
     {
       auto zaxisID1 = vlistZaxis(vlistID, index);
       auto zaxisID2 = zaxisDuplicate(zaxisID1);
-      auto zaxistype = zaxisInqType(zaxisID1);
 
       auto nlev = zaxisInqSize(zaxisID1);
       if (nlev <= 1) continue;
@@ -49,9 +48,10 @@ invertLevDes(int vlistID)
           zaxisDefUbounds(zaxisID2, yb2.data());
         }
 
+      auto zaxistype = zaxisInqType(zaxisID1);
       if (zaxistype == ZAXIS_HYBRID || zaxistype == ZAXIS_HYBRID_HALF)
         {
-          const int vctsize = zaxisInqVctSize(zaxisID1);
+          auto vctsize = zaxisInqVctSize(zaxisID1);
           if (vctsize && vctsize % 2 == 0)
             {
               Varray<double> vct1(vctsize), vct2(vctsize);
@@ -69,8 +69,20 @@ invertLevDes(int vlistID)
     }
 }
 
-class ModuleInvertlev
+class Invertlev : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Invertlev",
+    .operators = { { "invertlev", InvertlevHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Invertlev> registration = RegisterEntry<Invertlev>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -84,20 +96,16 @@ class ModuleInvertlev
   int nvars;
 
   bool dataIsUnchanged;
-  std::vector<std::vector<size_t>> varnmiss;
+  std::vector<std::vector<size_t>> varnumMissVals;
 
-  size_t nmiss;
+  size_t numMissVals;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
-    cdo_operator_add("invertlev", 0, 0, nullptr);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -121,24 +129,25 @@ public:
     nvars = vlistNvars(vlistID1);
 
     vardata = Varray2D<double>(nvars);
-    varnmiss = std::vector<std::vector<size_t>>(nvars);
+    varnumMissVals = Varray2D<size_t>(nvars);
 
     varList_init(varList1, vlistID1);
 
     auto has3dVar = false;
     for (int varID = 0; varID < nvars; ++varID)
       {
-        auto nlevels = varList1[varID].nlevels;
-        if (nlevels > 1)
+        const auto &var = varList1[varID];
+        if (var.nlevels > 1)
           {
             has3dVar = true;
-            vardata[varID].resize(varList1[varID].gridsize * nlevels);
-            varnmiss[varID].resize(nlevels);
+            vardata[varID].resize(var.gridsize * var.nlevels);
+            varnumMissVals[varID].resize(var.nlevels);
           }
       }
 
     if (!has3dVar) cdo_warning("No variables with invertable levels found!");
   }
+
   void
   run()
   {
@@ -160,8 +169,8 @@ public:
             if (vardata[varID].size())
               {
                 auto offset = varList1[varID].gridsize * levelID;
-                cdo_read_record(streamID1, &vardata[varID][offset], &nmiss);
-                varnmiss[varID][levelID] = nmiss;
+                cdo_read_record(streamID1, &vardata[varID][offset], &numMissVals);
+                varnumMissVals[varID][levelID] = numMissVals;
               }
             else
               {
@@ -169,8 +178,8 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    cdo_read_record(streamID1, array.data(), &nmiss);
-                    cdo_write_record(streamID2, array.data(), nmiss);
+                    cdo_read_record(streamID1, array.data(), &numMissVals);
+                    cdo_write_record(streamID2, array.data(), numMissVals);
                   }
               }
           }
@@ -179,16 +188,15 @@ public:
           {
             if (vardata[varID].size())
               {
-                auto gridsize = varList1[varID].gridsize;
-                auto nlevels = varList1[varID].nlevels;
-                for (int levelID = 0; levelID < nlevels; ++levelID)
+                const auto &var = varList1[varID];
+                for (int levelID = 0; levelID < var.nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
 
-                    auto offset = gridsize * (nlevels - levelID - 1);
-                    nmiss = varnmiss[varID][nlevels - levelID - 1];
+                    auto offset = var.gridsize * (var.nlevels - levelID - 1);
+                    numMissVals = varnumMissVals[varID][var.nlevels - levelID - 1];
 
-                    cdo_write_record(streamID2, &vardata[varID][offset], nmiss);
+                    cdo_write_record(streamID2, &vardata[varID][offset], numMissVals);
                   }
               }
           }
@@ -196,23 +204,11 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Invertlev(void *process)
-{
-  ModuleInvertlev invertlev;
-
-  invertlev.init(process);
-  invertlev.run();
-  invertlev.close();
-
-  return nullptr;
-}
diff --git a/src/Lic.cc b/src/Lic.cc
index 82d6cbfa56092c5446d9f360dbbdc287be40cba2..e8cb8a7e46684434e0ccbdb345c3f49a7fe848ec 100644
--- a/src/Lic.cc
+++ b/src/Lic.cc
@@ -165,7 +165,7 @@ WriteImage2PNG(int width, int height, unsigned char *pImage, float *mag, const c
     }
 
   // Initialize write structure
-  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr);
   if (png_ptr == nullptr)
     {
       fprintf(stderr, "Could not allocate write struct\n");
@@ -401,7 +401,7 @@ lic1(const char *obasename, int num, int n_xres, int n_yres, float *pVectr)
   // const char *fname = "LIC.ppm";
   // WriteImage2PPM(n_xres, n_yres, pImage, fname);
   char filename[8192];
-  sprintf(filename, "%s%04d.png", obasename, num);
+  std::snprintf(filename, sizeof(filename), "%s%04d.png", obasename, num);
 
 #ifdef HAVE_LIBPNG
   WriteImage2PNG(n_xres, n_yres, pImage, mag, filename);
@@ -416,10 +416,21 @@ lic1(const char *obasename, int num, int n_xres, int n_yres, float *pVectr)
   free(mag);
 }
 
-class ModuleLic
+class Lic : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Lic",
+    .operators = { { "lic"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Lic> registration = RegisterEntry<Lic>(module);
   int nlev = 0;
-  size_t nmiss;
+  size_t numMissVals;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -447,11 +458,9 @@ class ModuleLic
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     // clang-format off
   // int LIC  = cdo_operator_add("lic",  0, 0, nullptr);
     // clang-format on
@@ -531,8 +540,8 @@ public:
 
             if (varID == varID1 || varID == varID2)
               {
-                cdo_read_record(streamID1, array1.data(), &nmiss);
-                if (nmiss) cdo_abort("Missing values unsupported!");
+                cdo_read_record(streamID1, array1.data(), &numMissVals);
+                if (numMissVals) cdo_abort("Missing values unsupported!");
 
                 gridsize = gridInqSize(gridID);
 
@@ -563,21 +572,9 @@ public:
   {
 
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
 
-void *
-Lic(void *process)
-{
-  ModuleLic lic;
-  lic.init(process);
-  lic.run();
-  lic.close();
-  return nullptr;
-}
-
 // mencoder
 // png + alpha canel libpng
 // ls -1v | grep JPG > files.txt
diff --git a/src/Longinfo.cc b/src/Longinfo.cc
index 5f5e2f194874f8aa5ff0635eb0e77bb4e963b9e2..6951ce7779b2dd6c5252e69908daa19ce8f5c942 100644
--- a/src/Longinfo.cc
+++ b/src/Longinfo.cc
@@ -68,7 +68,7 @@ compute_stat_real(const Field &field, LonginfoStat &infostat, size_t gridsize)
 {
   size_t imiss = 0;
 
-  if (field.nmiss)
+  if (field.numMissVals)
     {
       auto nvals = field_min_max_sum_mv(field, infostat.min, infostat.max, infostat.sum);
       imiss = gridsize - nvals;
@@ -89,8 +89,19 @@ compute_stat_real(const Field &field, LonginfoStat &infostat, size_t gridsize)
   return imiss;
 }
 
-class ModuleLonginfo
+class Longinfo : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Longinfo",
+    .operators = { { "linfo"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Longinfo> registration = RegisterEntry<Longinfo>(module);
   char paramstr[32];
   CdoStreamID streamID;
 
@@ -102,12 +113,10 @@ class ModuleLonginfo
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  cdo_operator_add("linfo",   0,  0, nullptr);
     // clang-format on
 
     operator_check_argc(0);
@@ -153,14 +162,14 @@ public:
 
             field.init(var);
             cdo_read_record(streamID, field);
-            auto nmiss = field.nmiss;
+            auto numMissVals = field.numMissVals;
 
             LonginfoStat infostat;
 
             fprintf(stdout, "\t\tdataType: %s\n", cdo::datatype_to_cstr(var.datatype));
             fprintf(stdout, "\t\tmemoryType: %s\n", (var.memType == MemType::Float) ? "float" : "double");
             fprintf(stdout, "\t\tgridsize: %zu\n", var.gridsize);
-            fprintf(stdout, "\t\tnumMiss: %zu\n", nmiss);
+            fprintf(stdout, "\t\tnumMiss: %zu\n", numMissVals);
             fprintf(stdout, "\t\tmissval: %.*g\n", dig, var.missval);
 
             double addoffset = 0.0, scalefactor = 1.0;
@@ -188,7 +197,7 @@ public:
             cdoPrintAttributes(stdout, vlistID, varID, 16);
           }
 
-        // if (imiss != nmiss && nmiss) cdo_warning("Found %zu of %zu missing values!", imiss, nmiss);
+        // if (imiss != numMissVals && numMissVals) cdo_warning("Found %zu of %zu missing values!", imiss, numMissVals);
 
         tsID++;
       }
@@ -197,17 +206,5 @@ public:
   close()
   {
     cdo_stream_close(streamID);
-
-    cdo_finish();
   }
 };
-
-void *
-Longinfo(void *process)
-{
-  ModuleLonginfo longinfo;
-  longinfo.init(process);
-  longinfo.run();
-  longinfo.close();
-  return nullptr;
-}
diff --git a/src/Maggraph.cc b/src/Maggraph.cc
index 2b1683a13b9c917dd994e7d19a5705416e0e9211..cdd008ae9bba898715f7263a447e8957e4f82fb5 100644
--- a/src/Maggraph.cc
+++ b/src/Maggraph.cc
@@ -26,7 +26,7 @@
 
 #define DBG 0
 
-const char *line_colours[] = {
+static const char *line_colours[] = {
   "red",
   "green",
   "blue",
@@ -82,10 +82,10 @@ const char *line_colours[] = {
   "purple",
 };
 
-const char *graph_params[] = { "ymin", "ymax", "sigma", "stat", "obsv", "device", "linewidth" };
+static const char *graph_params[] = { "ymin", "ymax", "sigma", "stat", "obsv", "device", "linewidth" };
 
-int graph_param_count = sizeof(graph_params) / sizeof(char *);
-int num_colours = sizeof(line_colours) / sizeof(char *);
+constexpr int graph_param_count = sizeof(graph_params) / sizeof(char *);
+constexpr int num_colours = sizeof(line_colours) / sizeof(char *);
 
 int checkdevice(char *device_in);
 
@@ -131,7 +131,7 @@ compareTime(CdiTime time1, CdiTime time2)
 
 static void
 maggraph(const char *plotfile, const std::string &varname, const std::string &varunits, long nfiles, std::vector<long> nts,
-         std::vector<std::vector<CdiDateTime>> vDateTimes, std::vector<std::vector<double>> datatab, int nparam,
+         const std::vector<std::vector<CdiDateTime>> &vDateTimes, std::vector<std::vector<double>> datatab, int nparam,
          std::vector<std::string> &params)
 {
   char min_date_time_str[1024], max_date_time_str[1024];
@@ -271,8 +271,6 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
         }
     }
 
-  if (DBG) fprintf(stderr, "STAT  %d\n", (int) stat);
-
   char ***date_time_str = (char ***) malloc(nfiles * sizeof(char **));
 
   std::vector<double> date_time;
@@ -294,7 +292,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
         {
           date_time[tsID] = tsID + 1;
           date_time_str[0][tsID] = (char *) malloc(256);
-          sprintf(date_time_str[0][tsID], "%s", datetime_to_string(vDateTimes[0][tsID]).c_str());
+          std::snprintf(date_time_str[0][tsID], 256, "%s", datetime_to_string(vDateTimes[0][tsID]).c_str());
           mean_val[tsID] = 0.;
           std_dev_val[tsID] = 0.;
 
@@ -302,7 +300,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
 
           for (fileID = 0; fileID < nfiles; ++fileID)
             {
-              if (DBG) fprintf(stderr, "fileID=%ld\n", fileID);
+              if (DBG) fprintf(stderr, "fileID=%ld  ", fileID);
 
               if (datatab[fileID][tsID] < min_val) min_val = datatab[fileID][tsID];
               if (datatab[fileID][tsID] > max_val) max_val = datatab[fileID][tsID];
@@ -377,7 +375,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
               date_time[tsID] = tsID + 1;
 
               date_time_str[fileID][tsID] = (char *) malloc(256);
-              sprintf(date_time_str[fileID][tsID], "%s", datetime_to_string(vDateTimes[fileID][tsID]).c_str());
+              std::snprintf(date_time_str[fileID][tsID], 256, "%s", datetime_to_string(vDateTimes[fileID][tsID]).c_str());
               if (DBG && (tsID == 0 || tsID == nts[fileID] - 1)) fprintf(stderr, "%s\n", date_time_str[fileID][tsID]);
 
               if (datatab[fileID][tsID] < min_val) min_val = datatab[fileID][tsID];
@@ -528,8 +526,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
     {
       count = obsv ? i - 1 : i;
       if (DBG) fprintf(stderr, "Current File %ld\n", i);
-      // sprintf(legend_text_data, "ens_%d", count + 1);
-      sprintf(legend_text_data, "data_%d", count + 1);
+      std::snprintf(legend_text_data, sizeof(legend_text_data), "data_%d", count + 1);
       mag_setc("graph_line_colour", line_colours[count % num_colours]);
       mag_setc("legend_user_text", legend_text_data);
       if (stat)
@@ -549,7 +546,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
   if (obsv)
     {
       mag_setc("graph_line_colour", "black");
-      sprintf(legend_text_data, "%s", "Obsv");
+      std::snprintf(legend_text_data, sizeof(legend_text_data), "%s", "Obsv");
       mag_setc("legend_user_text", legend_text_data);
       mag_set1c("graph_curve_date_x_values", (const char **) date_time_str[0], nts[0]);
 
@@ -581,7 +578,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
       // TEMPORARY FIX, UNITL NEW MAGICS LIBRARY RELEASE *  end
 
       mag_set1r("graph_curve_y_values", mean_val.data(), ntime_steps);
-      sprintf(legend_text_data, "Mean");
+      std::snprintf(legend_text_data, sizeof(legend_text_data), "Mean");
       mag_setc("legend_user_text", legend_text_data);
       mag_graph();
 
@@ -595,7 +592,7 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
       mag_set1c("graph_curve_date_x_values", (const char **) date_time_str[0], ntime_steps);
       mag_set1r("graph_curve_y_values", spread_min.data(), ntime_steps);
       mag_setc("graph_shade_colour", "grey");
-      sprintf(legend_text_data, "%dSigma", num_sigma);
+      std::snprintf(legend_text_data, sizeof(legend_text_data), "%dSigma", num_sigma);
       mag_setc("legend_user_text", legend_text_data);
 
       // TEMPORARY FIX, UNITL NEW MAGICS LIBRARY RELEASE *  begin
@@ -609,11 +606,11 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
   char *lines[1];
   lines[0] = (char *) malloc(1024);
   // To be obtained from Meta Data
-  // sprintf( lines[0],"%s","ExpID : " );
-  // sprintf( lines[0],"%sxxxx  Variable : %s[%s]",lines[0], varname.c_str(), varunits.c_str() );
-  // sprintf( lines[0],"Variable : %s[%s]",varname.c_str(), varunits.c_str() );
-  // sprintf( lines[0],"%s  Date : %s --%s",lines[0], min_date_time_str, max_date_time_str );
-  sprintf(lines[0], "Variable : %s[%s]  Date : %s --%s", varname.c_str(), varunits.c_str(), min_date_time_str, max_date_time_str);
+  // std::snprintf(lines[0], 1024, "%s","ExpID : " );
+  // std::snprintf(lines[0], 1024, "%sxxxx  Variable : %s[%s]",lines[0], varname.c_str(), varunits.c_str() );
+  // std::snprintf(lines[0], 1024, "Variable : %s[%s]",varname.c_str(), varunits.c_str() );
+  // std::snprintf(lines[0], 1024, "%s  Date : %s --%s",lines[0], min_date_time_str, max_date_time_str );
+  std::snprintf(lines[0], 1024, "Variable : %s[%s]  Date : %s --%s", varname.c_str(), varunits.c_str(), min_date_time_str, max_date_time_str);
   mag_set1c("text_lines", (const char **) lines, 1);
 
   mag_setc("text_html", "true");
@@ -654,7 +651,7 @@ quit_MAGICS()
 }
 
 static void
-VerifyGraphParameters(int num_param, std::vector<std::string> &param_names)
+verifyGraphParameters(int num_param, std::vector<std::string> &param_names)
 {
   int i, j;
   bool found = false, syntax = true, halt_flag = false;
@@ -756,8 +753,19 @@ VerifyGraphParameters(int num_param, std::vector<std::string> &param_names)
 }
 #endif
 
-class ModuleMaggraph
+class Maggraph : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Maggraph",
+    .operators = { { "graph", MaggraphHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Maggraph> registration = RegisterEntry<Maggraph>(module);
 
   std::string varname, units;
   int gridID;
@@ -771,16 +779,13 @@ class ModuleMaggraph
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
 #ifdef HAVE_LIBMAGICS
-
     nparam = cdo_operator_argc();
     pnames = cdo_get_oper_argv();
 
-    if (nparam) VerifyGraphParameters(nparam, pnames);
+    if (nparam) verifyGraphParameters(nparam, pnames);
 
     nfiles = cdo_stream_cnt() - 1;
     ofilename = cdo_get_stream_name(nfiles);
@@ -848,9 +853,9 @@ public:
 
             int varID, levelID;
             cdo_inq_record(streamID, &varID, &levelID);
-            size_t nmiss;
+            size_t numMissVals;
             double val;
-            cdo_read_record(streamID, &val, &nmiss);
+            cdo_read_record(streamID, &val, &numMissVals);
             datatab[fileID][tsID] = val;
             vDateTimes[fileID][tsID] = taxisInqVdatetime(taxisID);
 
@@ -867,11 +872,6 @@ public:
     init_MAGICS();
 
     cdo_print("Creating PLOT for %s", varname);
-    if (DBG)
-      {
-        fprintf(stderr, "Num params %d\n", nparam);
-        for (int i = 0; i < nparam; ++i) fprintf(stderr, "Param %s\n", pnames[i].c_str());
-      }
 
     maggraph(ofilename, varname, units, nfiles, nts, vDateTimes, datatab, nparam, pnames);
 
@@ -890,17 +890,5 @@ public:
     quit_MAGICS();
     if (vlistID0 != -1) vlistDestroy(vlistID0);
 #endif
-    cdo_finish();
   }
 };
-
-void *
-Maggraph(void *process)
-{
-  ModuleMaggraph maggraph;
-  maggraph.init(process);
-  maggraph.run();
-  maggraph.close();
-
-  return nullptr;
-}
diff --git a/src/Magplot.cc b/src/Magplot.cc
index 14d3da2684f8cd6633744f3d20b511f33c4803b2..8e2f4d9de72a90aa1c5198e7c426af11946d34ee 100644
--- a/src/Magplot.cc
+++ b/src/Magplot.cc
@@ -36,8 +36,6 @@ subpage_upper_right_latitude
 subpage_upper_right_longitude
 ****/
 
-static int CONTOUR, SHADED, GRFILL;
-
 static const char *contour_params[]
     = { "min",    "max",       "count",      "interval", "list",    "colour",  "thickness", "style",     "RGB",
         "device", "step_freq", "file_split", "lat_min",  "lat_max", "lon_min", "lon_max",   "projection" };
@@ -169,348 +167,6 @@ static double LON_MIN = 1.0e+200, LON_MAX = -1.e+200;
 const char *COLOUR = nullptr, *COLOUR_MIN = nullptr, *COLOUR_MAX = nullptr, *STYLE = nullptr;
 const char *DEVICE = nullptr, *COLOUR_TRIAD = nullptr, *PROJECTION = nullptr;
 
-static void
-magplot(const char *plotfile, int operatorID, const std::string &varname, const std::string &units, long nlon, long nlat,
-        Varray<double> &grid_center_lon, Varray<double> &grid_center_lat, Varray<double> &array, size_t nmiss, double missval,
-        int nparam, const std::vector<std::string> &params, const std::string &datetimeStr, bool lregular)
-
-{
-  double dlon = 0, dlat = 0;
-  char plotfilename[4096];
-  char *titlename;
-  char tempname[256];
-
-  if (DBG)
-    {
-      fprintf(stderr, "Num params %d\n", nparam);
-
-      for (int i = 0; i < nparam; ++i) fprintf(stderr, "Param %s\n", params[i].c_str());
-      fflush(stderr);
-
-      for (int i = 0; i < nparam; ++i)
-        {
-          const auto splitStrings = split_with_seperator(params[i], '=');
-          const auto &key = splitStrings[0];
-
-          if (key == "min") fprintf(stderr, "Min Val %g\n", YMIN);
-          if (key == "max") fprintf(stderr, "Max Val %g\n", YMAX);
-          // if (key == "resolution") fprintf(stderr,"RESOLUTION %g\n",RESOLUTION );
-          if (key == "colour") fprintf(stderr, "COLOUR %s\n", COLOUR);
-          if (key == "colour_min") fprintf(stderr, "COLOUR %s\n", COLOUR_MIN);
-          if (key == "colour_max") fprintf(stderr, "COLOUR %s\n", COLOUR_MAX);
-          if (key == "interval") fprintf(stderr, "INTERVAL %f\n", INTERVAL);
-          if (key == "count") fprintf(stderr, "COUNT %d\n", COUNT);
-
-          if (key == "list")
-            for (int j = 0; j < NUM_LEVELS; ++j) fprintf(stderr, "LIST %f\n", LEV_LIST[j]);
-
-          if (key == "thickness") fprintf(stderr, "THICKNESS %d\n", THICKNESS);
-          if (key == "style") fprintf(stderr, "STYLE %s\n", STYLE);
-          if (key == "device") fprintf(stderr, "DEVICE %s\n", DEVICE);
-          if (key == "step_freq") fprintf(stderr, "STEP_FREQ %d\n", STEP_FREQ);
-          if (key == "lat_min") fprintf(stderr, "Lat Min Val %g\n", LAT_MIN);
-          if (key == "lat_max") fprintf(stderr, "Lat Max Val %g\n", LAT_MAX);
-          if (key == "lon_min") fprintf(stderr, "Lon Min Val %g\n", LON_MIN);
-          if (key == "lon_max") fprintf(stderr, "Lon Max Val %g\n", LON_MAX);
-          if (key == "projection") fprintf(stderr, "PROJECTION %s\n", PROJECTION);
-        }
-    }
-
-  if (lregular)
-    {
-      if (nlon > 1)
-        {
-          for (int i = 1; i < nlon; ++i) dlon += (grid_center_lon[i] - grid_center_lon[i - 1]);
-          dlon /= (nlon - 1);
-        }
-      if (nlat > 1)
-        {
-          for (int i = 1; i < nlat; ++i) dlat += (grid_center_lat[nlon * i] - grid_center_lat[nlon * (i - 1)]);
-          dlat /= (nlat - 1);
-        }
-    }
-
-  sprintf(plotfilename, "%s [%s] %s", varname.c_str(), units.c_str(), datetimeStr.c_str());
-  titlename = strdup(plotfilename);
-  sprintf(plotfilename, "%s_%s", plotfile, varname.c_str());
-
-  if (Options::cdoVerbose) cdo_print("nlon: %zu  nlat: %zu", nlon, nlat);
-  if (Options::cdoVerbose) cdo_print("dlon: %g  dlat: %g", dlon, dlat);
-
-  auto mm = nmiss ? varray_min_max_mv(nlon * nlat, array, missval) : varray_min_max(nlon * nlat, array);
-
-  if (Options::cdoVerbose) cdo_print("min: %g  max: %g", mm.min, mm.max);
-  if (Options::cdoVerbose) cdo_print("input_field_organization: %s", lregular ? "REGULAR" : "NONREGULAR");
-
-  mag_setc("output_name", plotfilename);
-  mag_new("page");
-
-  // Set the input data arrays to magics++
-
-  mag_set2r("input_field", array.data(), nlon, nlat);
-
-  mag_setr("input_field_suppress_below", mm.min);
-  mag_setr("input_field_suppress_above", mm.max);
-
-  if (lregular)
-    {
-      mag_setc("input_field_organization", "REGULAR");
-      // mag_setc("input_field_organization", "GAUSSIAN");
-
-      mag_setr("input_field_initial_latitude", grid_center_lat[0]);
-      mag_setr("input_field_latitude_step", dlat);
-
-      mag_setr("input_field_initial_longitude", grid_center_lon[0]);
-      mag_setr("input_field_longitude_step", dlon);
-    }
-  else
-    {
-      mag_setc("input_field_organization", "NONREGULAR");
-
-      mag_set2r("input_field_latitudes", grid_center_lat.data(), nlon, nlat);
-      mag_set2r("input_field_longitudes", grid_center_lon.data(), nlon, nlat);
-    }
-
-  /* magics_template_parser( magics_node ); */
-  /* results_template_parser(results_node, varname.c_str() ); */
-
-  /* set up the coastline attributes */
-  /* mag_setc ("map_coastline_colour", "khaki"); */
-  /* mag_setc ("map_grid_colour",      "grey");  */
-
-  // Parameters common to all operators
-  if (DEVICE) mag_setc("output_format", DEVICE);
-
-  if (PROJECTION) mag_setc("subpage_map_projection", PROJECTION);
-
-  mag_seti("map_label_latitude_frequency", 2);
-  mag_seti("map_label_longitude_frequency", 2);
-  /*mag_setr ("map_label_height",0.5);*/
-  mag_setr("map_label_height", 0.4);
-
-  /* define the contouring parameters */
-  if (operatorID == SHADED)
-    {
-      mag_setc("contour", "off");
-      mag_setc("contour_shade", "on");
-      mag_setc("contour_shade_method", "area_fill");
-      mag_setc("contour_label", "off");
-
-      if (LAT_MIN < 1.0e+200) mag_setr("subpage_lower_left_latitude", LAT_MIN);
-      if (LON_MIN < 1.0e+200) mag_setr("subpage_lower_left_longitude", LON_MIN);
-      if (LAT_MAX > -1.0e+200) mag_setr("subpage_upper_right_latitude", LAT_MAX);
-      if (LON_MAX > -1.0e+200) mag_setr("subpage_upper_right_longitude", LON_MAX);
-
-      if (YMIN < 1.0e+200)
-        {
-          mag_setr("contour_shade_min_level", YMIN);
-          mag_setr("contour_min_level", YMIN);
-        }
-
-      if (YMAX > -1.0e+200)
-        {
-          mag_setr("contour_shade_max_level", YMAX);
-          mag_setr("contour_max_level", YMAX);
-        }
-
-      if (COLOUR_MAX) mag_setc("contour_shade_max_level_colour", COLOUR_MAX);
-      if (COLOUR_MIN) mag_setc("contour_shade_min_level_colour", COLOUR_MIN);
-
-      if (is_not_equal(INTERVAL, 8.0f))
-        {
-          mag_setc("contour_level_selection_type", "INTERVAL");
-          mag_setr("contour_interval", INTERVAL);
-        }
-
-      if (COUNT != 10)
-        {
-          mag_setc("contour_level_selection_type", "COUNT");
-          mag_seti("contour_level_count", COUNT);
-        }
-
-      if (NUM_LEVELS)
-        {
-          mag_setc("contour_level_selection_type", "LEVEL_LIST");
-          mag_set1r("contour_level_list", LEV_LIST, NUM_LEVELS);
-        }
-
-      if (USR_COLOUR_COUNT)
-        {
-          mag_setc("contour_shade_colour_method", "LIST");
-          mag_set1c("contour_shade_colour_list", (const char **) USR_COLOUR_TABLE, USR_COLOUR_COUNT);
-        }
-
-      if (COLOUR_TRIAD) { mag_setc("contour_shade_colour_direction", COLOUR_TRIAD); }
-
-      // Adjust Set The page slightly to fit the legend
-      mag_setr("subpage_x_length", 24.);
-      mag_setr("subpage_y_length", 30.);
-
-      // Legend Settings
-      mag_setc("legend", "on");
-      mag_setc("legend_display_type", "continuous");
-      mag_setc("legend_entry_plot_direction", "column");
-      mag_setc("legend_box_mode", "positional");
-      mag_setr("legend_box_x_position", 26.5);
-      mag_setr("legend_box_y_position", 0.39);
-      mag_setr("legend_box_x_length", 2.0);
-      mag_setr("legend_box_y_length", 12.69);
-
-      if (DBG)
-        {
-          mag_enqc("output_name", (char *) &tempname);
-          fprintf(stderr, " SHADED Done %s!\n", tempname);
-          fprintf(stderr, " SHADED Done!\n");
-        }
-    }
-  else if (operatorID == CONTOUR)
-    {
-      mag_setc("contour", "on");
-      mag_setc("contour_shade", "off");
-      mag_setc("contour_label", "on");
-      mag_setc("contour_highlight", "off");
-
-      if (LAT_MIN < 1.0e+200) mag_setr("subpage_lower_left_latitude", LAT_MIN);
-      if (LON_MIN < 1.0e+200) mag_setr("subpage_lower_left_longitude", LON_MIN);
-      if (LAT_MAX > -1.0e+200) mag_setr("subpage_upper_right_latitude", LAT_MAX);
-      if (LON_MAX > -1.0e+200) mag_setr("subpage_upper_right_longitude", LON_MAX);
-
-      if (YMIN < 1.0e+200) mag_setr("contour_min_level", YMIN);
-      if (YMAX > -1.0e+200) mag_setr("contour_max_level", YMAX);
-
-      if (COLOUR) mag_setc("contour_line_colour", COLOUR);
-
-      if (is_not_equal(INTERVAL, 8.0f))
-        {
-          mag_setc("contour_level_selection_type", "INTERVAL");
-          mag_setr("contour_interval", INTERVAL);
-        }
-
-      if (COUNT != 10)
-        {
-          mag_setc("contour_level_selection_type", "COUNT");
-          mag_seti("contour_level_count", COUNT);
-        }
-
-      if (NUM_LEVELS)
-        {
-          mag_setc("contour_level_selection_type", "LEVEL_LIST");
-          mag_set1r("contour_level_list", LEV_LIST, NUM_LEVELS);
-        }
-
-      if (THICKNESS != 1) mag_seti("contour_line_thickness", THICKNESS);
-
-      if (STYLE) mag_setc("contour_line_style", STYLE);
-
-      // Adjust Set The page slightly to fit the legend
-      mag_setr("subpage_x_length", 24.);
-      mag_setr("subpage_y_length", 30.);
-
-      if (DBG) fprintf(stderr, " CONTOUR Done!\n");
-    }
-  else if (operatorID == GRFILL)
-    {
-      mag_setc("contour", "off");
-      mag_setc("contour_shade", "on");
-
-      // mag_setc ( "contour_shade_technique", "cell_shading" );
-      mag_setc("contour_shade_technique", "grid_shading");
-
-      mag_setc("contour_shade_method", "area_fill");
-      mag_setc("contour_label", "off");
-
-      if (LAT_MIN < 1.0e+200) mag_setr("subpage_lower_left_latitude", LAT_MIN);
-      if (LON_MIN < 1.0e+200) mag_setr("subpage_lower_left_longitude", LON_MIN);
-      if (LAT_MAX > -1.0e+200) mag_setr("subpage_upper_right_latitude", LAT_MAX);
-      if (LON_MAX > -1.0e+200) mag_setr("subpage_upper_right_longitude", LON_MAX);
-
-      if (YMIN < 1.0e+200)
-        {
-          mag_setr("contour_shade_min_level", YMIN);
-          mag_setr("contour_min_level", YMIN);
-        }
-
-      if (YMAX > -1.0e+200)
-        {
-          mag_setr("contour_shade_max_level", YMAX);
-          mag_setr("contour_max_level", YMAX);
-        }
-
-      // if( YMIN < 1.0e+200  ) mag_setr( "contour_shade_min_level", YMIN );
-      // if( YMAX > -1.0e+200 ) mag_setr( "contour_shade_max_level", YMAX );
-
-      if (COLOUR_MIN) mag_setc("contour_shade_min_level_colour", COLOUR_MIN);
-      if (COLOUR_MAX) mag_setc("contour_shade_max_level_colour", COLOUR_MAX);
-
-      if (is_not_equal(INTERVAL, 8.0f))
-        {
-          mag_setc("contour_level_selection_type", "INTERVAL");
-          mag_setr("contour_interval", INTERVAL);
-        }
-
-      if (COUNT != 10)
-        {
-          mag_setc("contour_level_selection_type", "COUNT");
-          mag_seti("contour_level_count", COUNT);
-        }
-
-      if (NUM_LEVELS)
-        {
-          mag_setc("contour_level_selection_type", "LEVEL_LIST");
-          mag_set1r("contour_level_list", LEV_LIST, NUM_LEVELS);
-        }
-
-      if (USR_COLOUR_COUNT)
-        {
-          mag_setc("contour_shade_colour_method", "LIST");
-          mag_set1c("contour_shade_colour_list", (const char **) USR_COLOUR_TABLE, USR_COLOUR_COUNT);
-        }
-      /*
-      if( is_not_equal(RESOLUTION, 10.0f) )
-        mag_setr( "contour_shade_cell_resolution", RESOLUTION );
-      */
-      if (COLOUR_TRIAD) mag_setc("contour_shade_colour_direction", COLOUR_TRIAD);
-
-      // Adjust Set The page slightly to fit the legend
-      mag_setr("subpage_x_length", 24.);
-      mag_setr("subpage_y_length", 30.);
-
-      // Legend Settings
-      mag_setc("legend", "on");
-      mag_setc("legend_display_type", "continuous");
-      mag_setc("legend_entry_plot_direction", "column");
-      mag_setc("legend_box_mode", "positional");
-      mag_setr("legend_box_x_position", 26.5);
-      mag_setr("legend_box_y_position", 0.39);
-      mag_setr("legend_box_x_length", 2.0);
-      mag_setr("legend_box_y_length", 12.69);
-
-      if (DBG) fprintf(stderr, " GrFILL Done!\n");
-    }
-
-  // plot the title text and the coastlines
-  mag_cont();
-  mag_coast();
-
-  mag_set1c("text_lines", (const char **) &titlename, 1);
-  mag_setc("text_colour", "black");
-
-  /*
-    mag_setr("text_font_size", 0.6);
-    mag_setc("text_mode", "positional");
-    mag_setr("text_box_x_position", 1.5);
-    mag_setr("text_box_y_position", 16.5);
-    mag_setr("text_box_x_length", 20.);
-    mag_setr("text_box_y_length", 2.5);
-    mag_setc("text_border", "off");
-  */
-
-  mag_setc("text_justification", "left");
-  mag_text();
-
-  if (LEV_LIST) free(LEV_LIST);
-}
-
 static void
 init_MAGICS()
 {
@@ -530,220 +186,6 @@ quit_MAGICS()
   if (DBG) fprintf(stderr, "Exiting From MAGICS\n");
 }
 
-static void
-verifyPlotParameters(int num_param, const std::vector<std::string> &param_names, int opID)
-{
-  bool halt_flag = false;
-  int param_count = 0;
-  const char **params = nullptr;
-  char *temp_str;
-  const char orig_char = ';', rep_char = ',';
-
-  /*
-    char *contour_params[] = {"ymin","ymax","count","interval","list","colour","thickness","style"};
-    char *shaded_params[]  = {"ymin","ymax","count","interval","list","colour_min","colour_max","colour_table","step_freq"};
-    char *grfill_params[]  = {"ymin","ymax","count","interval","list","colour_min","colour_max","colour_table","resolution"};
-  */
-
-  for (int i = 0; i < num_param; ++i)
-    {
-      auto found = false;
-      auto syntax = true;
-      const auto splitStrings = split_with_seperator(param_names[i], '=');
-
-      if (DBG) fprintf(stderr, "Verifying params!\n");
-
-      if (splitStrings.size() > 1)
-        {
-          const auto &key = splitStrings[0];
-          auto value = strdup(splitStrings[1].c_str());
-
-          if (opID == CONTOUR)
-            {
-              param_count = contour_param_count;
-              params = contour_params;
-            }
-          else if (opID == SHADED)
-            {
-              param_count = shaded_param_count;
-              params = shaded_params;
-            }
-          else if (opID == GRFILL)
-            {
-              param_count = grfill_param_count;
-              params = grfill_params;
-            }
-
-          for (int j = 0; j < param_count; ++j)
-            {
-              if (key == params[j])
-                {
-                  found = true;
-                  if (key == "colour" || key == "style" || key == "colour_min" || key == "colour_max" || key == "RGB"
-                      || key == "colour_triad" || key == "device" || key == "file_split" || key == "projection")
-                    {
-                      if (string_is_float(value)) { syntax = false; }
-                      else
-                        {
-                          if (key == "RGB" || key == "file_split")
-                            {
-                              temp_str = strdup(value);
-                              cstr_to_lower(temp_str);
-                              if (strcmp(temp_str, "true") && strcmp(temp_str, "false")) { syntax = false; }
-                              else
-                                {
-                                  if (key == "RGB")
-                                    isRGB = cdo_cmpstr(temp_str, "true");
-                                  else if (key == "file_split")
-                                    FILE_SPLIT = cdo_cmpstr(temp_str, "true");
-                                }
-                            }
-                          else if (key == "style")
-                            {
-                              if (checkstyle(value)) syntax = false;
-                            }
-                          else if (key == "colour" || key == "colour_min" || key == "colour_max")
-                            {
-
-                              if (checkcolour(value)) { syntax = false; }
-                              else
-                                {
-                                  if (key == "colour")
-                                    {
-                                      temp_str = strdup(value);
-                                      if (!isRGB)
-                                        cstr_to_lower(temp_str);
-                                      else
-                                        {
-                                          cstr_to_upper(temp_str);
-                                          cstr_replace_char(temp_str, orig_char, rep_char);  // replace ';' in RGB format to ','
-                                        }
-                                      COLOUR = temp_str;
-                                      if (DBG) fprintf(stderr, "COLOUR %s\n", COLOUR);
-                                    }
-                                  if (key == "colour_min")
-                                    {
-                                      temp_str = strdup(value);
-                                      if (!isRGB)
-                                        cstr_to_lower(temp_str);
-                                      else
-                                        {
-                                          cstr_to_upper(temp_str);
-                                          cstr_replace_char(temp_str, orig_char, rep_char);  // replace ';' in RGB format to ','
-                                        }
-                                      COLOUR_MIN = temp_str;
-                                      if (DBG) fprintf(stderr, "COLOUR %s\n", COLOUR_MIN);
-                                    }
-                                  if (key == "colour_max")
-                                    {
-                                      temp_str = strdup(value);
-                                      if (!isRGB)
-                                        cstr_to_lower(temp_str);
-                                      else
-                                        {
-                                          cstr_to_upper(temp_str);
-                                          cstr_replace_char(temp_str, orig_char, rep_char);  // replace ';' in RGB format to ','
-                                        }
-                                      COLOUR_MAX = temp_str;
-                                      if (DBG) fprintf(stderr, "COLOUR %s\n", COLOUR_MAX);
-                                    }
-                                }
-                            }
-                          else if (key == "device")
-                            {
-                              if (checkdevice(value)) syntax = false;
-                            }
-                          else if (key == "colour_triad")
-                            {
-                              temp_str = strdup(value);
-                              cstr_to_upper(temp_str);
-                              if (strcmp(temp_str, "CW") && strcmp(temp_str, "ACW"))
-                                syntax = false;
-                              else
-                                {
-                                  if (DBG) fprintf(stderr, "TRIAD check  %s!\n", temp_str);
-                                  COLOUR_TRIAD = cdo_cmpstr(temp_str, "CW") ? "clockwise" : "anti_clockwise";
-                                }
-                            }
-                          else if (key == "projection")
-                            {
-                              if (checkprojection(value)) syntax = false;
-                            }
-                        }
-                    }
-
-                  if (key == "min" || key == "max" || key == "lat_min" || key == "lat_max" || key == "lon_min" || key == "lon_max"
-                      || key == "count" || key == "interval" || key == "thickness" || key == "resolution" || key == "step_freq")
-                    {
-                      if (!string_is_float(value))
-                        syntax = false;
-                      else
-                        {
-                          if (key == "min") YMIN = atof(value);
-                          if (key == "max") YMAX = atof(value);
-                          if (key == "count") COUNT = atoi(value);
-                          if (key == "interval") INTERVAL = atof(value);
-                          if (key == "thickness") THICKNESS = atoi(value);
-                          if (key == "resolution") RESOLUTION = atoi(value);
-                          if (key == "step_freq") STEP_FREQ = atoi(value);
-                          if (key == "lat_min") LAT_MIN = atof(value);
-                          if (key == "lat_max") LAT_MAX = atof(value);
-                          if (key == "lon_min") LON_MIN = atof(value);
-                          if (key == "lon_max") LON_MAX = atof(value);
-                        }
-                    }
-
-                  if (key == "colour_table")
-                    {
-                      auto fp = std::fopen(value, "r");
-                      if (fp == nullptr)
-                        {
-                          fprintf(stderr, "Input Color Table File not found in specified path '%s'\n", value);
-                          halt_flag = true;
-                        }
-                      else { ReadColourTable(value); }
-                    }
-
-                  if (key == "list")
-                    {
-                      const auto splitStrings2 = split_with_seperator(value, ';');
-                      if (!splitStrings2.size()) { syntax = false; }
-                      else
-                        {
-                          for (size_t k = 0, n = splitStrings2.size(); k < n; ++k)
-                            {
-                              if (!string_is_float(splitStrings2[k])) syntax = false;
-                            }
-                          if (syntax == true)
-                            {
-                              NUM_LEVELS = (int) splitStrings2.size();
-                              LEV_LIST = (double *) malloc(NUM_LEVELS * sizeof(double));
-                              for (int k = 0; k < NUM_LEVELS; ++k) LEV_LIST[k] = std::stof(splitStrings2[k]);
-                            }
-                        }
-                    }
-                } /*** if(key == params[j])  ***/
-            }     /*** Loop over param count ***/
-
-          // if (value) free(value); // value is use e.g. for DEVICE
-        } /*** (splitStrings.size() > 1) ***/
-      else { syntax = false; }
-
-      if (!found)
-        {
-          halt_flag = true;
-          fprintf(stderr, "Invalid parameter  '%s'\n", param_names[i].c_str());
-        }
-      if (found && syntax == false)
-        {
-          halt_flag = true;
-          fprintf(stderr, "Invalid parameter specification  '%s'\n", param_names[i].c_str());
-        }
-    } /*** Loop over params ****/
-
-  if (halt_flag) exit(0);
-}
-
 int
 checkcolour(char *colour_in)
 {
@@ -960,8 +402,22 @@ checkprojection(char *projection_in)
 }
 #endif
 
-class ModuleMagplot
+class Magplot : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Magplot",
+    .operators = { { "contour", MagplotHelp },
+                   { "shaded", MagplotHelp },
+                   { "grfill", MagplotHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Magplot> registration = RegisterEntry<Magplot>(module);
+  int CONTOUR, SHADED, GRFILL;
   CdoStreamID streamID;
 
   int operatorID;
@@ -984,21 +440,579 @@ class ModuleMagplot
 
   Varray<double> array;
 
-  int CONTOUR, SHADED, GRFILL;
+#ifdef HAVE_LIBMAGICS
+  void
+  verifyPlotParameters(int num_param, const std::vector<std::string> &param_names, int opID)
+  {
+    bool halt_flag = false;
+    int param_count = 0;
+    const char **params = nullptr;
+    char *temp_str;
+    const char orig_char = ';', rep_char = ',';
+
+    /*
+      char *contour_params[] = {"ymin","ymax","count","interval","list","colour","thickness","style"};
+      char *shaded_params[]  = {"ymin","ymax","count","interval","list","colour_min","colour_max","colour_table","step_freq"};
+      char *grfill_params[]  = {"ymin","ymax","count","interval","list","colour_min","colour_max","colour_table","resolution"};
+    */
+
+    for (int i = 0; i < num_param; ++i)
+      {
+        auto found = false;
+        auto syntax = true;
+        const auto splitStrings = split_with_seperator(param_names[i], '=');
+
+        if (DBG) fprintf(stderr, "Verifying params!\n");
+
+        if (splitStrings.size() > 1)
+          {
+            const auto &key = splitStrings[0];
+            auto value = strdup(splitStrings[1].c_str());
+
+            if (opID == CONTOUR)
+              {
+                param_count = contour_param_count;
+                params = contour_params;
+              }
+            else if (opID == SHADED)
+              {
+                param_count = shaded_param_count;
+                params = shaded_params;
+              }
+            else if (opID == GRFILL)
+              {
+                param_count = grfill_param_count;
+                params = grfill_params;
+              }
+
+            for (int j = 0; j < param_count; ++j)
+              {
+                if (key == params[j])
+                  {
+                    found = true;
+                    if (key == "colour" || key == "style" || key == "colour_min" || key == "colour_max" || key == "RGB"
+                        || key == "colour_triad" || key == "device" || key == "file_split" || key == "projection")
+                      {
+                        if (string_is_float(value)) { syntax = false; }
+                        else
+                          {
+                            if (key == "RGB" || key == "file_split")
+                              {
+                                temp_str = strdup(value);
+                                cstr_to_lower(temp_str);
+                                if (strcmp(temp_str, "true") && strcmp(temp_str, "false")) { syntax = false; }
+                                else
+                                  {
+                                    if (key == "RGB")
+                                      isRGB = cdo_cmpstr(temp_str, "true");
+                                    else if (key == "file_split")
+                                      FILE_SPLIT = cdo_cmpstr(temp_str, "true");
+                                  }
+                              }
+                            else if (key == "style")
+                              {
+                                if (checkstyle(value)) syntax = false;
+                              }
+                            else if (key == "colour" || key == "colour_min" || key == "colour_max")
+                              {
+
+                                if (checkcolour(value)) { syntax = false; }
+                                else
+                                  {
+                                    if (key == "colour")
+                                      {
+                                        temp_str = strdup(value);
+                                        if (!isRGB)
+                                          cstr_to_lower(temp_str);
+                                        else
+                                          {
+                                            cstr_to_upper(temp_str);
+                                            cstr_replace_char(temp_str, orig_char, rep_char);  // replace ';' in RGB format to ','
+                                          }
+                                        COLOUR = temp_str;
+                                        if (DBG) fprintf(stderr, "COLOUR %s\n", COLOUR);
+                                      }
+                                    if (key == "colour_min")
+                                      {
+                                        temp_str = strdup(value);
+                                        if (!isRGB)
+                                          cstr_to_lower(temp_str);
+                                        else
+                                          {
+                                            cstr_to_upper(temp_str);
+                                            cstr_replace_char(temp_str, orig_char, rep_char);  // replace ';' in RGB format to ','
+                                          }
+                                        COLOUR_MIN = temp_str;
+                                        if (DBG) fprintf(stderr, "COLOUR %s\n", COLOUR_MIN);
+                                      }
+                                    if (key == "colour_max")
+                                      {
+                                        temp_str = strdup(value);
+                                        if (!isRGB)
+                                          cstr_to_lower(temp_str);
+                                        else
+                                          {
+                                            cstr_to_upper(temp_str);
+                                            cstr_replace_char(temp_str, orig_char, rep_char);  // replace ';' in RGB format to ','
+                                          }
+                                        COLOUR_MAX = temp_str;
+                                        if (DBG) fprintf(stderr, "COLOUR %s\n", COLOUR_MAX);
+                                      }
+                                  }
+                              }
+                            else if (key == "device")
+                              {
+                                if (checkdevice(value)) syntax = false;
+                              }
+                            else if (key == "colour_triad")
+                              {
+                                temp_str = strdup(value);
+                                cstr_to_upper(temp_str);
+                                if (strcmp(temp_str, "CW") && strcmp(temp_str, "ACW"))
+                                  syntax = false;
+                                else
+                                  {
+                                    if (DBG) fprintf(stderr, "TRIAD check  %s!\n", temp_str);
+                                    COLOUR_TRIAD = cdo_cmpstr(temp_str, "CW") ? "clockwise" : "anti_clockwise";
+                                  }
+                              }
+                            else if (key == "projection")
+                              {
+                                if (checkprojection(value)) syntax = false;
+                              }
+                          }
+                      }
+
+                    if (key == "min" || key == "max" || key == "lat_min" || key == "lat_max" || key == "lon_min" || key == "lon_max"
+                        || key == "count" || key == "interval" || key == "thickness" || key == "resolution" || key == "step_freq")
+                      {
+                        if (!string_is_float(value))
+                          syntax = false;
+                        else
+                          {
+                            if (key == "min") YMIN = atof(value);
+                            if (key == "max") YMAX = atof(value);
+                            if (key == "count") COUNT = atoi(value);
+                            if (key == "interval") INTERVAL = atof(value);
+                            if (key == "thickness") THICKNESS = atoi(value);
+                            if (key == "resolution") RESOLUTION = atoi(value);
+                            if (key == "step_freq") STEP_FREQ = atoi(value);
+                            if (key == "lat_min") LAT_MIN = atof(value);
+                            if (key == "lat_max") LAT_MAX = atof(value);
+                            if (key == "lon_min") LON_MIN = atof(value);
+                            if (key == "lon_max") LON_MAX = atof(value);
+                          }
+                      }
+
+                    if (key == "colour_table")
+                      {
+                        auto fp = std::fopen(value, "r");
+                        if (fp == nullptr)
+                          {
+                            fprintf(stderr, "Input Color Table File not found in specified path '%s'\n", value);
+                            halt_flag = true;
+                          }
+                        else { ReadColourTable(value); }
+                      }
+
+                    if (key == "list")
+                      {
+                        const auto splitStrings2 = split_with_seperator(value, ';');
+                        if (!splitStrings2.size()) { syntax = false; }
+                        else
+                          {
+                            for (size_t k = 0, n = splitStrings2.size(); k < n; ++k)
+                              {
+                                if (!string_is_float(splitStrings2[k])) syntax = false;
+                              }
+                            if (syntax == true)
+                              {
+                                NUM_LEVELS = (int) splitStrings2.size();
+                                LEV_LIST = (double *) malloc(NUM_LEVELS * sizeof(double));
+                                for (int k = 0; k < NUM_LEVELS; ++k) LEV_LIST[k] = std::stof(splitStrings2[k]);
+                              }
+                          }
+                      }
+                  } /*** if(key == params[j])  ***/
+              }     /*** Loop over param count ***/
+
+            // if (value) free(value); // value is use e.g. for DEVICE
+          } /*** (splitStrings.size() > 1) ***/
+        else { syntax = false; }
+
+        if (!found)
+          {
+            halt_flag = true;
+            fprintf(stderr, "Invalid parameter  '%s'\n", param_names[i].c_str());
+          }
+        if (found && syntax == false)
+          {
+            halt_flag = true;
+            fprintf(stderr, "Invalid parameter specification  '%s'\n", param_names[i].c_str());
+          }
+      } /*** Loop over params ****/
+
+    if (halt_flag) exit(0);
+  }
+#endif
+
+#ifdef HAVE_LIBMAGICS
+  void
+  magplot(const char *plotfile, int p_operatorID, const std::string &varname, const std::string &units, long p_nlon, long p_nlat,
+          Varray<double> &p_grid_center_lon, Varray<double> &p_grid_center_lat, Varray<double> &p_array, size_t numMissVals,
+          double missval, int p_nparam, const std::vector<std::string> &params, const std::string &datetimeStr, bool p_lregular)
+
+  {
+    double dlon = 0, dlat = 0;
+    char plotfilename[4096];
+    char *titlename;
+    char tempname[256];
+
+    if (DBG)
+      {
+        fprintf(stderr, "Num params %d\n", p_nparam);
+
+        for (int i = 0; i < p_nparam; ++i) fprintf(stderr, "Param %s\n", params[i].c_str());
+        fflush(stderr);
+
+        for (int i = 0; i < p_nparam; ++i)
+          {
+            const auto splitStrings = split_with_seperator(params[i], '=');
+            const auto &key = splitStrings[0];
+
+            if (key == "min") fprintf(stderr, "Min Val %g\n", YMIN);
+            if (key == "max") fprintf(stderr, "Max Val %g\n", YMAX);
+            // if (key == "resolution") fprintf(stderr,"RESOLUTION %g\n",RESOLUTION );
+            if (key == "colour") fprintf(stderr, "COLOUR %s\n", COLOUR);
+            if (key == "colour_min") fprintf(stderr, "COLOUR %s\n", COLOUR_MIN);
+            if (key == "colour_max") fprintf(stderr, "COLOUR %s\n", COLOUR_MAX);
+            if (key == "interval") fprintf(stderr, "INTERVAL %f\n", INTERVAL);
+            if (key == "count") fprintf(stderr, "COUNT %d\n", COUNT);
+
+            if (key == "list")
+              for (int j = 0; j < NUM_LEVELS; ++j) fprintf(stderr, "LIST %f\n", LEV_LIST[j]);
+
+            if (key == "thickness") fprintf(stderr, "THICKNESS %d\n", THICKNESS);
+            if (key == "style") fprintf(stderr, "STYLE %s\n", STYLE);
+            if (key == "device") fprintf(stderr, "DEVICE %s\n", DEVICE);
+            if (key == "step_freq") fprintf(stderr, "STEP_FREQ %d\n", STEP_FREQ);
+            if (key == "lat_min") fprintf(stderr, "Lat Min Val %g\n", LAT_MIN);
+            if (key == "lat_max") fprintf(stderr, "Lat Max Val %g\n", LAT_MAX);
+            if (key == "lon_min") fprintf(stderr, "Lon Min Val %g\n", LON_MIN);
+            if (key == "lon_max") fprintf(stderr, "Lon Max Val %g\n", LON_MAX);
+            if (key == "projection") fprintf(stderr, "PROJECTION %s\n", PROJECTION);
+          }
+      }
+
+    if (p_lregular)
+      {
+        if (p_nlon > 1)
+          {
+            for (int i = 1; i < p_nlon; ++i) dlon += (p_grid_center_lon[i] - p_grid_center_lon[i - 1]);
+            dlon /= (p_nlon - 1);
+          }
+        if (p_nlat > 1)
+          {
+            for (int i = 1; i < p_nlat; ++i) dlat += (p_grid_center_lat[p_nlon * i] - p_grid_center_lat[p_nlon * (i - 1)]);
+            dlat /= (p_nlat - 1);
+          }
+      }
+
+    std::snprintf(plotfilename, sizeof(plotfilename), "%s [%s] %s", varname.c_str(), units.c_str(), datetimeStr.c_str());
+    titlename = strdup(plotfilename);
+    std::snprintf(plotfilename, sizeof(plotfilename), "%s_%s", plotfile, varname.c_str());
+
+    if (Options::cdoVerbose) cdo_print("p_nlon: %zu  p_nlat: %zu", p_nlon, p_nlat);
+    if (Options::cdoVerbose) cdo_print("dlon: %g  dlat: %g", dlon, dlat);
+
+    auto mm = numMissVals ? varray_min_max_mv(p_nlon * p_nlat, p_array, missval) : varray_min_max(p_nlon * p_nlat, p_array);
+
+    if (Options::cdoVerbose) cdo_print("min: %g  max: %g", mm.min, mm.max);
+    if (Options::cdoVerbose) cdo_print("input_field_organization: %s", p_lregular ? "REGULAR" : "NONREGULAR");
+
+    mag_setc("output_name", plotfilename);
+    mag_new("page");
+
+    // Set the input data arrays to magics++
+
+    mag_set2r("input_field", p_array.data(), p_nlon, p_nlat);
+
+    mag_setr("input_field_suppress_below", mm.min);
+    mag_setr("input_field_suppress_above", mm.max);
+
+    if (lregular)
+      {
+        mag_setc("input_field_organization", "REGULAR");
+        // mag_setc("input_field_organization", "GAUSSIAN");
+
+        mag_setr("input_field_initial_latitude", p_grid_center_lat[0]);
+        mag_setr("input_field_latitude_step", dlat);
+
+        mag_setr("input_field_initial_longitude", p_grid_center_lon[0]);
+        mag_setr("input_field_longitude_step", dlon);
+      }
+    else
+      {
+        mag_setc("input_field_organization", "NONREGULAR");
+
+        mag_set2r("input_field_latitudes", p_grid_center_lat.data(), p_nlon, p_nlat);
+        mag_set2r("input_field_longitudes", p_grid_center_lon.data(), p_nlon, p_nlat);
+      }
+
+    /* magics_template_parser( magics_node ); */
+    /* results_template_parser(results_node, varname.c_str() ); */
+
+    /* set up the coastline attributes */
+    /* mag_setc ("map_coastline_colour", "khaki"); */
+    /* mag_setc ("map_grid_colour",      "grey");  */
+
+    // Parameters common to all operators
+    if (DEVICE) mag_setc("output_format", DEVICE);
+
+    if (PROJECTION) mag_setc("subpage_map_projection", PROJECTION);
+
+    mag_seti("map_label_latitude_frequency", 2);
+    mag_seti("map_label_longitude_frequency", 2);
+    /*mag_setr ("map_label_height",0.5);*/
+    mag_setr("map_label_height", 0.4);
+
+    /* define the contouring parameters */
+    if (p_operatorID == SHADED)
+      {
+        mag_setc("contour", "off");
+        mag_setc("contour_shade", "on");
+        mag_setc("contour_shade_method", "area_fill");
+        mag_setc("contour_label", "off");
+
+        if (LAT_MIN < 1.0e+200) mag_setr("subpage_lower_left_latitude", LAT_MIN);
+        if (LON_MIN < 1.0e+200) mag_setr("subpage_lower_left_longitude", LON_MIN);
+        if (LAT_MAX > -1.0e+200) mag_setr("subpage_upper_right_latitude", LAT_MAX);
+        if (LON_MAX > -1.0e+200) mag_setr("subpage_upper_right_longitude", LON_MAX);
+
+        if (YMIN < 1.0e+200)
+          {
+            mag_setr("contour_shade_min_level", YMIN);
+            mag_setr("contour_min_level", YMIN);
+          }
+
+        if (YMAX > -1.0e+200)
+          {
+            mag_setr("contour_shade_max_level", YMAX);
+            mag_setr("contour_max_level", YMAX);
+          }
+
+        if (COLOUR_MAX) mag_setc("contour_shade_max_level_colour", COLOUR_MAX);
+        if (COLOUR_MIN) mag_setc("contour_shade_min_level_colour", COLOUR_MIN);
+
+        if (is_not_equal(INTERVAL, 8.0f))
+          {
+            mag_setc("contour_level_selection_type", "INTERVAL");
+            mag_setr("contour_interval", INTERVAL);
+          }
+
+        if (COUNT != 10)
+          {
+            mag_setc("contour_level_selection_type", "COUNT");
+            mag_seti("contour_level_count", COUNT);
+          }
+
+        if (NUM_LEVELS)
+          {
+            mag_setc("contour_level_selection_type", "LEVEL_LIST");
+            mag_set1r("contour_level_list", LEV_LIST, NUM_LEVELS);
+          }
+
+        if (USR_COLOUR_COUNT)
+          {
+            mag_setc("contour_shade_colour_method", "LIST");
+            mag_set1c("contour_shade_colour_list", (const char **) USR_COLOUR_TABLE, USR_COLOUR_COUNT);
+          }
+
+        if (COLOUR_TRIAD) { mag_setc("contour_shade_colour_direction", COLOUR_TRIAD); }
+
+        // Adjust Set The page slightly to fit the legend
+        mag_setr("subpage_x_length", 24.);
+        mag_setr("subpage_y_length", 30.);
+
+        // Legend Settings
+        mag_setc("legend", "on");
+        mag_setc("legend_display_type", "continuous");
+        mag_setc("legend_entry_plot_direction", "column");
+        mag_setc("legend_box_mode", "positional");
+        mag_setr("legend_box_x_position", 26.5);
+        mag_setr("legend_box_y_position", 0.39);
+        mag_setr("legend_box_x_length", 2.0);
+        mag_setr("legend_box_y_length", 12.69);
+
+        if (DBG)
+          {
+            mag_enqc("output_name", (char *) &tempname);
+            fprintf(stderr, " SHADED Done %s!\n", tempname);
+            fprintf(stderr, " SHADED Done!\n");
+          }
+      }
+    else if (p_operatorID == CONTOUR)
+      {
+        mag_setc("contour", "on");
+        mag_setc("contour_shade", "off");
+        mag_setc("contour_label", "on");
+        mag_setc("contour_highlight", "off");
+
+        if (LAT_MIN < 1.0e+200) mag_setr("subpage_lower_left_latitude", LAT_MIN);
+        if (LON_MIN < 1.0e+200) mag_setr("subpage_lower_left_longitude", LON_MIN);
+        if (LAT_MAX > -1.0e+200) mag_setr("subpage_upper_right_latitude", LAT_MAX);
+        if (LON_MAX > -1.0e+200) mag_setr("subpage_upper_right_longitude", LON_MAX);
+
+        if (YMIN < 1.0e+200) mag_setr("contour_min_level", YMIN);
+        if (YMAX > -1.0e+200) mag_setr("contour_max_level", YMAX);
+
+        if (COLOUR) mag_setc("contour_line_colour", COLOUR);
+
+        if (is_not_equal(INTERVAL, 8.0f))
+          {
+            mag_setc("contour_level_selection_type", "INTERVAL");
+            mag_setr("contour_interval", INTERVAL);
+          }
+
+        if (COUNT != 10)
+          {
+            mag_setc("contour_level_selection_type", "COUNT");
+            mag_seti("contour_level_count", COUNT);
+          }
+
+        if (NUM_LEVELS)
+          {
+            mag_setc("contour_level_selection_type", "LEVEL_LIST");
+            mag_set1r("contour_level_list", LEV_LIST, NUM_LEVELS);
+          }
+
+        if (THICKNESS != 1) mag_seti("contour_line_thickness", THICKNESS);
+
+        if (STYLE) mag_setc("contour_line_style", STYLE);
+
+        // Adjust Set The page slightly to fit the legend
+        mag_setr("subpage_x_length", 24.);
+        mag_setr("subpage_y_length", 30.);
+
+        if (DBG) fprintf(stderr, " CONTOUR Done!\n");
+      }
+    else if (p_operatorID == GRFILL)
+      {
+        mag_setc("contour", "off");
+        mag_setc("contour_shade", "on");
+
+        // mag_setc ( "contour_shade_technique", "cell_shading" );
+        mag_setc("contour_shade_technique", "grid_shading");
+
+        mag_setc("contour_shade_method", "area_fill");
+        mag_setc("contour_label", "off");
+
+        if (LAT_MIN < 1.0e+200) mag_setr("subpage_lower_left_latitude", LAT_MIN);
+        if (LON_MIN < 1.0e+200) mag_setr("subpage_lower_left_longitude", LON_MIN);
+        if (LAT_MAX > -1.0e+200) mag_setr("subpage_upper_right_latitude", LAT_MAX);
+        if (LON_MAX > -1.0e+200) mag_setr("subpage_upper_right_longitude", LON_MAX);
+
+        if (YMIN < 1.0e+200)
+          {
+            mag_setr("contour_shade_min_level", YMIN);
+            mag_setr("contour_min_level", YMIN);
+          }
+
+        if (YMAX > -1.0e+200)
+          {
+            mag_setr("contour_shade_max_level", YMAX);
+            mag_setr("contour_max_level", YMAX);
+          }
+
+        // if( YMIN < 1.0e+200  ) mag_setr( "contour_shade_min_level", YMIN );
+        // if( YMAX > -1.0e+200 ) mag_setr( "contour_shade_max_level", YMAX );
+
+        if (COLOUR_MIN) mag_setc("contour_shade_min_level_colour", COLOUR_MIN);
+        if (COLOUR_MAX) mag_setc("contour_shade_max_level_colour", COLOUR_MAX);
+
+        if (is_not_equal(INTERVAL, 8.0f))
+          {
+            mag_setc("contour_level_selection_type", "INTERVAL");
+            mag_setr("contour_interval", INTERVAL);
+          }
+
+        if (COUNT != 10)
+          {
+            mag_setc("contour_level_selection_type", "COUNT");
+            mag_seti("contour_level_count", COUNT);
+          }
+
+        if (NUM_LEVELS)
+          {
+            mag_setc("contour_level_selection_type", "LEVEL_LIST");
+            mag_set1r("contour_level_list", LEV_LIST, NUM_LEVELS);
+          }
+
+        if (USR_COLOUR_COUNT)
+          {
+            mag_setc("contour_shade_colour_method", "LIST");
+            mag_set1c("contour_shade_colour_list", (const char **) USR_COLOUR_TABLE, USR_COLOUR_COUNT);
+          }
+        /*
+        if( is_not_equal(RESOLUTION, 10.0f) )
+          mag_setr( "contour_shade_cell_resolution", RESOLUTION );
+        */
+        if (COLOUR_TRIAD) mag_setc("contour_shade_colour_direction", COLOUR_TRIAD);
+
+        // Adjust Set The page slightly to fit the legend
+        mag_setr("subpage_x_length", 24.);
+        mag_setr("subpage_y_length", 30.);
+
+        // Legend Settings
+        mag_setc("legend", "on");
+        mag_setc("legend_display_type", "continuous");
+        mag_setc("legend_entry_plot_direction", "column");
+        mag_setc("legend_box_mode", "positional");
+        mag_setr("legend_box_x_position", 26.5);
+        mag_setr("legend_box_y_position", 0.39);
+        mag_setr("legend_box_x_length", 2.0);
+        mag_setr("legend_box_y_length", 12.69);
+
+        if (DBG) fprintf(stderr, " GrFILL Done!\n");
+      }
+
+    // plot the title text and the coastlines
+    mag_cont();
+    mag_coast();
+
+    mag_set1c("text_lines", (const char **) &titlename, 1);
+    mag_setc("text_colour", "black");
+
+    /*
+      mag_setr("text_font_size", 0.6);
+      mag_setc("text_mode", "positional");
+      mag_setr("text_box_x_position", 1.5);
+      mag_setr("text_box_y_position", 16.5);
+      mag_setr("text_box_x_length", 20.);
+      mag_setr("text_box_y_length", 2.5);
+      mag_setc("text_border", "off");
+    */
+
+    mag_setc("text_justification", "left");
+    mag_text();
+
+    if (LEV_LIST) free(LEV_LIST);
+  }
+
+#endif
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
 #ifdef HAVE_LIBMAGICS
     nparam = cdo_operator_argc();
     pnames = cdo_get_oper_argv();
 
-    CONTOUR = cdo_operator_add("contour", 0, 0, nullptr);
-    SHADED = cdo_operator_add("shaded", 0, 0, nullptr);
-    GRFILL = cdo_operator_add("grfill", 0, 0, nullptr);
+    CONTOUR = module.get_id("contour");
+    SHADED = module.get_id("shaded");
+    GRFILL = module.get_id("grfill");
 
     operatorID = cdo_operator_id();
 
@@ -1109,11 +1123,11 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID, array.data(), &numMissVals);
 
             const auto missval = vlistInqVarMissval(vlistID, varID);
-            if (nmiss) cdo_set_nan(missval, gridsize, array.data());
+            if (numMissVals) cdo_set_nan(missval, gridsize, array.data());
 
             auto varname = cdo::inq_var_name(vlistID, varID);
             auto units = cdo::inq_var_units(vlistID, varID);
@@ -1131,7 +1145,7 @@ public:
 
                 if (DBG) fprintf(stderr, "Plot %d\n", varID);
                 magplot(cdo_get_stream_name(1), operatorID, varname, units, nlon, nlat, grid_center_lon, grid_center_lat, array,
-                        nmiss, missval, nparam, pnames, datetimeStr, lregular);
+                        numMissVals, missval, nparam, pnames, datetimeStr, lregular);
               }
             else
               fprintf(stderr, "operator not implemented\n");
@@ -1175,18 +1189,5 @@ public:
 
     // quit_XML_template_parser( );
 #endif
-
-    cdo_finish();
   }
 };
-
-void *
-Magplot(void *process)
-{
-  ModuleMagplot magplot;
-  magplot.init(process);
-  magplot.run();
-  magplot.close();
-
-  return nullptr;
-}
diff --git a/src/Magvector.cc b/src/Magvector.cc
index 6399f5376e8e89ce5db6c9831a691d9ae12f2aad..dc02af294a8cb9f846a8fac36b527b1888cb2f27 100644
--- a/src/Magvector.cc
+++ b/src/Magvector.cc
@@ -25,7 +25,6 @@
 
 #define DBG 0
 
-int VECTOR, STREAM;
 const char *vector_params[] = { "thin_fac", "unit_vec", "device", "step_freq" };
 int vector_param_count = sizeof(vector_params) / sizeof(char *);
 
@@ -38,9 +37,31 @@ int checkdevice(char *device_in);
 extern const char *DEVICE;
 
 static void
-magvector(const char *plotfile, int operatorID, long nlon, long nlat, Varray<double> &grid_center_lon,
-          Varray<double> &grid_center_lat, Varray<double> &uarray, Varray<double> &varray, int nparam,
-          std::vector<std::string> &params, const std::string &datetimeStr)
+init_MAGICS()
+
+{
+
+  setenv("MAGPLUS_QUIET", "1", 1); /* To suppress magics messages */
+  mag_open();
+
+  /* Some standard parameters affectng the magics environment, moved from the xml file  ** begin ** */
+  mag_setc("page_id_line", "off");
+}
+
+static void
+quit_MAGICS()
+{
+  mag_close();
+  if (DBG) fprintf(stdout, "Exiting From MAGICS\n");
+}
+
+#endif
+
+#ifdef HAVE_LIBMAGICS
+void
+magvector(const char *plotfile, long nlon, long nlat, Varray<double> &grid_center_lon, Varray<double> &grid_center_lat,
+          Varray<double> &uarray, Varray<double> &varray, int nparam, std::vector<std::string> &params,
+          const std::string &datetimeStr)
 
 {
   long i;
@@ -70,7 +91,7 @@ magvector(const char *plotfile, int operatorID, long nlon, long nlat, Varray<dou
 
   for (i = 0; i < nparam; ++i)
     {
-      const auto splitStrings = split_with_seperator(params[i], '=');
+      auto splitStrings = split_with_seperator(params[i], '=');
       const auto &key = splitStrings[0];
       const auto &value = splitStrings[1];
 
@@ -119,9 +140,9 @@ magvector(const char *plotfile, int operatorID, long nlon, long nlat, Varray<dou
 
   /* results_template_parser(results_node, varname ); */
 
-  sprintf(plotfilename, "Velocity Vectors %s", datetimeStr.c_str());
+  std::snprintf(plotfilename, sizeof(plotfilename), "Velocity Vectors %s", datetimeStr.c_str());
   char *titlename = strdup(plotfilename);
-  sprintf(plotfilename, "%s", plotfile);
+  std::snprintf(plotfilename, sizeof(plotfilename), "%s", plotfile);
 
   mag_setc("output_name", plotfilename);
   mag_new("page");
@@ -141,56 +162,37 @@ magvector(const char *plotfile, int operatorID, long nlon, long nlat, Varray<dou
   /*mag_setr ("map_label_height",0.5);*/
   mag_setr("map_label_height", 0.4);
 
-  if (operatorID == VECTOR)
-    {
-      /* Magics functions for performing vector operation */
-      /*
-        mag_setc("wind_legend_only", "on" );
-        mag_setc("wind_legend_text", "on" );
-      */
+  // if (operatorID == VECTOR)
+  {
+    /* Magics functions for performing vector operation */
+    /*
+      mag_setc("wind_legend_only", "on" );
+      mag_setc("wind_legend_text", "on" );
+    */
 
-      mag_setc("legend", "on");
-      mag_setc("wind_flag_cross_boundary", "on");
-      mag_seti("wind_arrow_thickness", 1);
-      mag_coast();
+    mag_setc("legend", "on");
+    mag_setc("wind_flag_cross_boundary", "on");
+    mag_seti("wind_arrow_thickness", 1);
+    mag_coast();
 
-      if (is_not_equal(THIN_FAC, 2.0f)) mag_setr("wind_thinning_factor", THIN_FAC);
+    if (is_not_equal(THIN_FAC, 2.0f)) mag_setr("wind_thinning_factor", THIN_FAC);
 
-      /*wind_arrow_unit_velocity */
-      if (is_not_equal(UNIT_VEC, 25.0f)) mag_setr("wind_arrow_unit_velocity", UNIT_VEC);
+    /*wind_arrow_unit_velocity */
+    if (is_not_equal(UNIT_VEC, 25.0f)) mag_setr("wind_arrow_unit_velocity", UNIT_VEC);
 
-      mag_wind();
+    mag_wind();
 
-      mag_set1c("text_lines", (const char **) &titlename, 1);
-      mag_setc("text_colour", "black");
-      mag_setc("text_justification", "centre");
-      mag_text();
-    }
+    mag_set1c("text_lines", (const char **) &titlename, 1);
+    mag_setc("text_colour", "black");
+    mag_setc("text_justification", "centre");
+    mag_text();
+  }
 
   free(titlename);
 }
 
-static void
-init_MAGICS()
-
-{
-
-  setenv("MAGPLUS_QUIET", "1", 1); /* To suppress magics messages */
-  mag_open();
-
-  /* Some standard parameters affectng the magics environment, moved from the xml file  ** begin ** */
-  mag_setc("page_id_line", "off");
-}
-
-static void
-quit_MAGICS()
-{
-  mag_close();
-  if (DBG) fprintf(stdout, "Exiting From MAGICS\n");
-}
-
-static void
-VerifyVectorParameters(int num_param, std::vector<std::string> &param_names, int opID)
+void
+verify_vector_parameters(int num_param, std::vector<std::string> &param_names, bool vectorMode)
 {
 
   int i, j;
@@ -204,7 +206,7 @@ VerifyVectorParameters(int num_param, std::vector<std::string> &param_names, int
     {
       auto found = false;
       auto syntax = true;
-      const auto splitStrings = split_with_seperator(param_names[i], '=');
+      auto splitStrings = split_with_seperator(param_names[i], '=');
 
       if (DBG) fprintf(stderr, "Verifying params!\n");
 
@@ -212,7 +214,7 @@ VerifyVectorParameters(int num_param, std::vector<std::string> &param_names, int
         {
           const auto &key = splitStrings[0];
           const auto &value = splitStrings[1];
-          if (opID == VECTOR)
+          if (vectorMode)
             {
               param_count = vector_param_count;
               params = vector_params;
@@ -268,10 +270,22 @@ VerifyVectorParameters(int num_param, std::vector<std::string> &param_names, int
 }
 #endif
 
-class ModuleMagvector
+class Magvector : public Process
 {
-  CdoStreamID streamID;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Magvector",
+    .operators = { { "vector", MagvectorHelp }, { "stream", MagvectorHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Magvector> registration = RegisterEntry<Magvector>(module);
+
   int VECTOR, STREAM;
+  CdoStreamID streamID;
   int taxisID;
   int vlistID;
   int operatorID;
@@ -294,16 +308,14 @@ class ModuleMagvector
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
 #ifdef HAVE_LIBMAGICS
     nparam = cdo_operator_argc();
     pnames = cdo_get_oper_argv();
 
-    VECTOR = cdo_operator_add("vector", 0, 0, nullptr);
-    STREAM = cdo_operator_add("stream", 0, 0, nullptr);
+    VECTOR = module.get_id("vector");
+    STREAM = module.get_id("stream");
 
     operatorID = cdo_operator_id();
 
@@ -312,7 +324,7 @@ public:
         if (DBG)
           for (int i = 0; i < nparam; ++i) fprintf(stderr, "Param %d is %s!\n", i + 1, pnames[i].c_str());
 
-        VerifyVectorParameters(nparam, pnames, operatorID);
+        verify_vector_parameters(nparam, pnames, (operatorID == VECTOR));
       }
 
     streamID = cdo_open_read(0);
@@ -355,16 +367,16 @@ public:
     init_MAGICS();
 #endif
   }
+
   void
   run()
   {
 #ifdef HAVE_LIBMAGICS
-
     int tsID = 0;
 
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID, tsID);
         if (nrecs == 0) break;
 
         if (ANIM_FLAG)
@@ -384,7 +396,7 @@ public:
               }
           }
 
-        const auto datetimeStr = datetime_to_string(taxisInqVdatetime(taxisID));
+        auto datetimeStr = datetime_to_string(taxisInqVdatetime(taxisID));
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
@@ -398,17 +410,17 @@ public:
                 if (varname == "var131" || varname == "u")  // U Velocity as per GRIB is var131, as per NC 'u'
                   {
                     if (DBG) fprintf(stderr, "Found U VEL in Varname %s\n", varname.c_str());
-                    size_t nmiss;
-                    cdo_read_record(streamID, uarray.data(), &nmiss);
-                    if (nmiss) cdo_set_nan(vlistInqVarMissval(vlistID, varID), gridsize, uarray.data());
+                    size_t numMissVals;
+                    cdo_read_record(streamID, uarray.data(), &numMissVals);
+                    if (numMissVals) cdo_set_nan(vlistInqVarMissval(vlistID, varID), gridsize, uarray.data());
                     found++;
                   }
                 if (varname == "var132" || varname == "v")  // V Velocity as per GRIB  is var132, as per NC 'v'
                   {
                     if (DBG) fprintf(stderr, "Found V VEL in Varname %s\n", varname.c_str());
-                    size_t nmiss;
-                    cdo_read_record(streamID, varray.data(), &nmiss);
-                    if (nmiss) cdo_set_nan(vlistInqVarMissval(vlistID, varID), gridsize, varray.data());
+                    size_t numMissVals;
+                    cdo_read_record(streamID, varray.data(), &numMissVals);
+                    if (numMissVals) cdo_set_nan(vlistInqVarMissval(vlistID, varID), gridsize, varray.data());
                     found++;
                   }
                 if (found == 2) break;
@@ -424,8 +436,8 @@ public:
             if (found == 2)
               {
                 if (DBG) fprintf(stderr, "Found Both U & V VEL, Creating vector fields! \n");
-                magvector(cdo_get_stream_name(1), operatorID, nlon, nlat, grid_center_lon, grid_center_lat, uarray, varray, nparam,
-                          pnames, datetimeStr);
+                magvector(cdo_get_stream_name(1), nlon, nlat, grid_center_lon, grid_center_lat, uarray, varray, nparam, pnames,
+                          datetimeStr);
               }
             else if (found == 1)
               {
@@ -461,22 +473,8 @@ public:
   {
 #ifdef HAVE_LIBMAGICS
     cdo_stream_close(streamID);
-    /*   quit_XML_template_parser( ); */
+    // quit_XML_template_parser();
     quit_MAGICS();
 #endif
-
-    cdo_finish();
   }
 };
-
-void *
-Magvector(void *process)
-{
-
-  ModuleMagvector magvector;
-  magvector.init(process);
-  magvector.run();
-  magvector.close();
-
-  return nullptr;
-}
diff --git a/src/Makefile.am b/src/Makefile.am
index 5424e6273148a1026c63b09c8420808421878d31..642e3a94f2b3e770a72fb0fdfd0e25c150c60d04 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,8 +14,6 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				cdi_uuid.h                \
 				cdoStream.cc              \
 				cdoStream.h               \
-				cdo_apply.cc              \
-				cdo_apply.h	          \
 				cdo_cdi_wrapper.cc        \
 				cdo_cdi_wrapper.h         \
 				cdo_cmor.h                \
@@ -37,6 +35,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				cdo_history.h             \
 				cdo_math.cc               \
 				cdo_math.h                \
+				cdo_module.cc               \
+				cdo_module.h                \
 				cdo_options.cc            \
 				cdo_options.h             \
 				cdo_output.cc             \
@@ -88,7 +88,6 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				datarangelist.h           \
 				datetime.cc               \
 				datetime.h                \
-				dmemory.h                 \
 				ecacore.cc                \
 				ecacore.h                 \
 				ecautil.cc                \
@@ -156,12 +155,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				matrix_view.h             \
 				merge_axis.cc             \
 				merge_axis.h              \
-				module_definitions.h      \
-				module_list.h             \
 				module_info.cc            \
 				module_info.h             \
-				module_static_maps.h	  \
-				module_static_maps.cc 	  \
 				modules.cc                \
 				modules.h                 \
 				mpim_grid/grid_convert.h  \
@@ -182,6 +177,7 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				namelist.cc               \
 				namelist.h                \
 				nanoflann.hpp             \
+				operator_help.cc          \
 				operator_help.h           \
 				par_io.cc                 \
 				par_io.h                  \
@@ -215,8 +211,6 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				progress.h                \
 				pthread_debug.cc          \
 				pthread_debug.h           \
-				readline.cc               \
-				readline.h                \
 				region.h                  \
 				region.cc                 \
 				remap.h                   \
diff --git a/src/MapReduce.cc b/src/MapReduce.cc
index 6111c9b68203a770b9bc2342600e2d67a8e9272d..d04ec39af961b249d10f154cb4f4c9478cc36bd4 100644
--- a/src/MapReduce.cc
+++ b/src/MapReduce.cc
@@ -16,12 +16,12 @@
 static void
 read_first_record(const std::string &filename, double *field)
 {
-  size_t nmiss;
+  size_t numMissVals;
   int varID, levelID;
   auto streamID = stream_open_read_locked(filename.c_str());
   streamInqTimestep(streamID, 0);
   streamInqRecord(streamID, &varID, &levelID);
-  streamReadRecord(streamID, field, &nmiss);
+  streamReadRecord(streamID, field, &numMissVals);
   streamClose(streamID);
 }
 
@@ -39,8 +39,19 @@ countMask(const double *maskField, size_t gridSize, double falseVal)
   return counter;
 }
 
-class ModuleMapReduce
+class MapReduce : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "MapReduce",
+    .operators = { { "reducegrid", MapReduceHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<MapReduce> registration = RegisterEntry<MapReduce>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -59,9 +70,8 @@ class ModuleMapReduce
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
 
@@ -170,8 +180,8 @@ public:
                 auto varID2 = vlistFindVar(vlistID2, varID);
                 auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
 
-                size_t nmiss;
-                cdo_read_record(streamID1, arrayIn.data(), &nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID1, arrayIn.data(), &numMissVals);
 
                 for (size_t i = 0; i < maskSize; ++i) arrayOut[i] = arrayIn[maskIndexList[i]];
 
@@ -188,21 +198,9 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
 /*
  * the operators argument has to be a single horizontal field,
  * non-zero values are used to mark the relevant locations
  */
-void *
-MapReduce(void *process)
-{
-  ModuleMapReduce mapreduce;
-  mapreduce.init(process);
-  mapreduce.run();
-  mapreduce.close();
-
-  return nullptr;
-}
diff --git a/src/Maskbox.cc b/src/Maskbox.cc
index 44be74d5dae6780bd7f7f3179e12afec29ec907e..d2570d0443c51a58f3b5dadabdc1fa1c784975c3 100644
--- a/src/Maskbox.cc
+++ b/src/Maskbox.cc
@@ -19,7 +19,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "readline.h"
 #include <mpim_grid.h>
 #include "selboxinfo.h"
 #include "util_string.h"
@@ -220,8 +219,25 @@ get_processVars(int vlistID1, int gridID)
   return processVars;
 }
 
-class ModuleMaskbox
+class Maskbox : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Maskbox",
+    .operators = { { "masklonlatbox", 0, 0, "western and eastern longitude and southern and northern latitude", MaskboxHelp },
+                   { "maskindexbox", 0, 0, "index of first and last longitude and index of first and last latitude", MaskboxHelp },
+                   { "maskregion", 0, 0, "DCW region or the path to region file", MaskregionHelp },
+                   { "maskcircle", 0, 0, "Longitude, latitude of the center and radius of the circle", MaskregionHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Maskbox>(module);
+
+  int MASKLONLATBOX, MASKINDEXBOX, MASKREGION, MASKCIRCLE;
+
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -236,16 +252,12 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    auto MASKLONLATBOX = cdo_operator_add("masklonlatbox", 0, 0, "western and eastern longitude and southern and northern latitude");
-    auto MASKINDEXBOX  = cdo_operator_add("maskindexbox",  0, 0, "index of first and last longitude and index of first and last latitude");
-    auto MASKREGION    = cdo_operator_add("maskregion",    0, 0, "DCW region or the path to region file");
-    auto MASKCIRCLE    = cdo_operator_add("maskcircle",    0, 0, "Longitude, latitude of the center and radius of the circle");
-    // clang-format on
+    MASKLONLATBOX = module.get_id("masklonlatbox");
+    MASKINDEXBOX = module.get_id("maskindexbox");
+    MASKREGION = module.get_id("maskregion");
+    MASKCIRCLE = module.get_id("maskcircle");
 
     auto operatorID = cdo_operator_id();
     auto operIndexBox = (operatorID == MASKINDEXBOX);
@@ -305,9 +317,9 @@ public:
         for (int i = 0; i < numFiles; ++i)
           {
             Regions regions;
-            auto param = cdo_operator_argv(i).c_str();
-            if (std::strncmp(param, "dcw:", 4) == 0)
-              read_regions_from_dcw(param + 4, regions);
+            auto param = cdo_operator_argv(i);
+            if (param.starts_with("dcw:"))
+              read_regions_from_dcw(param.c_str() + 4, regions);
             else
               read_regions_from_file(param, regions);
 
@@ -402,18 +414,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Maskbox(void *process)
-{
-  ModuleMaskbox maskbox;
-  maskbox.init(process);
-  maskbox.run();
-  maskbox.close();
-
-  return nullptr;
-}
diff --git a/src/Mastrfu.cc b/src/Mastrfu.cc
index 7851d4e0b8ce9a66e327b6393259668390dfa67b..5906a494d58259f5ac53b41517dc96abbcb64bc0 100644
--- a/src/Mastrfu.cc
+++ b/src/Mastrfu.cc
@@ -19,7 +19,7 @@
 #include "cdo_zaxis.h"
 
 static void
-mastrfu(int gridID, int zaxisID, const Varray2D<double> &field1, Varray2D<double> &field2, size_t nmiss, double missval)
+mastrfu(int gridID, int zaxisID, const Varray2D<double> &field1, Varray2D<double> &field2, size_t numMissVals, double missval)
 {
   auto fact = 4.0 * std::atan(1.0) * 6371000.0 / 9.81;
 
@@ -47,7 +47,7 @@ mastrfu(int gridID, int zaxisID, const Varray2D<double> &field1, Varray2D<double
   for (int ilev = 0; ilev < nlev; ilev++)
     for (size_t ilat = 0; ilat < nlat; ilat++) field2[ilev][ilat] = 0.0;
 
-  if (nmiss == 0)
+  if (numMissVals == 0)
     {
       for (int ilev = nlev - 1; ilev >= 0; ilev--)
         for (int n = ilev; n < nlev - 1; ++n)
@@ -73,8 +73,19 @@ mastrfu(int gridID, int zaxisID, const Varray2D<double> &field1, Varray2D<double
     }
 }
 
-class ModuleMastrfu
+class Mastrfu : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Mastrfu",
+    .operators = { { "mastrfu", MastrfuHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Mastrfu> registration = RegisterEntry<Mastrfu>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -93,9 +104,8 @@ class ModuleMastrfu
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     streamID1 = cdo_open_read(0);
 
@@ -153,25 +163,25 @@ public:
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
 
-        size_t nmiss = 0;
+        size_t numMissVals = 0;
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss1;
-            cdo_read_record(streamID1, array1[levelID].data(), &nmiss1);
-            nmiss += nmiss1;
+            size_t numMissVals1;
+            cdo_read_record(streamID1, array1[levelID].data(), &numMissVals1);
+            numMissVals += numMissVals1;
           }
 
-        mastrfu(gridID, zaxisID, array1, array2, nmiss, missval);
+        mastrfu(gridID, zaxisID, array1, array2, numMissVals, missval);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID = 0;
             int levelID = recID;
             cdo_def_record(streamID2, varID, levelID);
-            nmiss = array_num_mv(nlat, array2[levelID].data(), missval);
-            cdo_write_record(streamID2, array2[levelID].data(), nmiss);
+            numMissVals = array_num_mv(nlat, array2[levelID].data(), missval);
+            cdo_write_record(streamID2, array2[levelID].data(), numMissVals);
           }
 
         tsID++;
@@ -182,19 +192,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Mastrfu(void *process)
-{
-  ModuleMastrfu mastrfu;
-
-  mastrfu.init(process);
-  mastrfu.run();
-  mastrfu.close();
-
-  return nullptr;
-}
diff --git a/src/Math.cc b/src/Math.cc
index 90fe4771183b709d63e42c033e2d72a79cdc1477..882baf41f48cef355023a935894f778384af4cdd 100644
--- a/src/Math.cc
+++ b/src/Math.cc
@@ -33,15 +33,15 @@
 #include "param_conversion.h"
 
 static void
-check_out_of_range(size_t &nmiss, const size_t len, double missval, Varray<double> &v, double rmin, double rmax)
+check_out_of_range(size_t &numMissVals, const size_t len, double missval, Varray<double> &v, double rmin, double rmax)
 {
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (!dbl_is_equal(v[i], missval) && (v[i] < rmin || v[i] > rmax))
           {
             v[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
   else
@@ -50,21 +50,21 @@ check_out_of_range(size_t &nmiss, const size_t len, double missval, Varray<doubl
         if (v[i] < rmin || v[i] > rmax)
           {
             v[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
 }
 
 static void
-check_lower_range(size_t &nmiss, const size_t len, double missval, Varray<double> &v, double rmin)
+check_lower_range(size_t &numMissVals, const size_t len, double missval, Varray<double> &v, double rmin)
 {
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (!dbl_is_equal(v[i], missval) && v[i] < rmin)
           {
             v[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
   else
@@ -73,17 +73,17 @@ check_lower_range(size_t &nmiss, const size_t len, double missval, Varray<double
         if (v[i] < rmin)
           {
             v[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
 }
 
 template <typename T, class UnaryOperation>
 void
-math_varray_transform(const size_t nmiss, const size_t len, double missval1, const Varray<T> &array1, Varray<T> &array2,
+math_varray_transform(const size_t numMissVals, const size_t len, double missval1, const Varray<T> &array1, Varray<T> &array2,
                       UnaryOperation unary_op)
 {
-  if (nmiss)
+  if (numMissVals)
     for (size_t i = 0; i < len; ++i) array2[i] = dbl_is_equal(array1[i], missval1) ? missval1 : unary_op(array1[i]);
   else
     for (size_t i = 0; i < len; ++i) array2[i] = unary_op(array1[i]);
@@ -171,36 +171,30 @@ enum struct Oper
   Arg
 };
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("abs",   (int)Oper::Abs,   0, nullptr);
-  cdo_operator_add("int",   (int)Oper::Int,   0, nullptr);
-  cdo_operator_add("nint",  (int)Oper::Nint,  0, nullptr);
-  cdo_operator_add("sqr",   (int)Oper::Sqr,   0, nullptr);
-  cdo_operator_add("sqrt",  (int)Oper::Sqrt,  0, nullptr);
-  cdo_operator_add("exp",   (int)Oper::Exp,   0, nullptr);
-  cdo_operator_add("ln",    (int)Oper::Ln,    0, nullptr);
-  cdo_operator_add("log10", (int)Oper::Log10, 0, nullptr);
-  cdo_operator_add("sin",   (int)Oper::Sin,   0, nullptr);
-  cdo_operator_add("cos",   (int)Oper::Cos,   0, nullptr);
-  cdo_operator_add("tan",   (int)Oper::Tan,   0, nullptr);
-  cdo_operator_add("asin",  (int)Oper::Asin,  0, nullptr);
-  cdo_operator_add("acos",  (int)Oper::Acos,  0, nullptr);
-  cdo_operator_add("atan",  (int)Oper::Atan,  0, nullptr);
-  cdo_operator_add("pow",   (int)Oper::Pow,   0, nullptr);
-  cdo_operator_add("rand",  (int)Oper::Rand,  0, nullptr);
-  cdo_operator_add("reci",  (int)Oper::Reci,  0, nullptr);
-  cdo_operator_add("not",   (int)Oper::Not,   0, nullptr);
-  cdo_operator_add("conj",  (int)Oper::Conj,  0, nullptr);
-  cdo_operator_add("re",    (int)Oper::Re,    0, nullptr);
-  cdo_operator_add("im",    (int)Oper::Im,    0, nullptr);
-  cdo_operator_add("arg",   (int)Oper::Arg,   0, nullptr);
-  // clang-format on
-}
-class ModuleMath
+class Math : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Math",
+    .operators = { { "abs", (int) Oper::Abs, 0, MathHelp },   { "int", (int) Oper::Int, 0, MathHelp },
+                   { "nint", (int) Oper::Nint, 0, MathHelp }, { "sqr", (int) Oper::Sqr, 0, MathHelp },
+                   { "sqrt", (int) Oper::Sqrt, 0, MathHelp }, { "exp", (int) Oper::Exp, 0, MathHelp },
+                   { "ln", (int) Oper::Ln, 0, MathHelp },     { "log10", (int) Oper::Log10, 0, MathHelp },
+                   { "sin", (int) Oper::Sin, 0, MathHelp },   { "cos", (int) Oper::Cos, 0, MathHelp },
+                   { "tan", (int) Oper::Tan, 0, MathHelp },   { "asin", (int) Oper::Asin, 0, MathHelp },
+                   { "acos", (int) Oper::Acos, 0, MathHelp }, { "atan", (int) Oper::Atan, 0, MathHelp },
+                   { "pow", (int) Oper::Pow, 0, MathHelp },   { "rand", (int) Oper::Rand, 0, MathHelp },
+                   { "reci", (int) Oper::Reci, 0, MathHelp }, { "not", (int) Oper::Not, 0, MathHelp },
+                   { "conj", (int) Oper::Conj, 0, MathHelp }, { "re", (int) Oper::Re, 0, MathHelp },
+                   { "im", (int) Oper::Im, 0, MathHelp },     { "arg", (int) Oper::Arg, 0, MathHelp } },
+    .aliases = { { "log", "ln" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Math> registration = RegisterEntry<Math>(module);
+
 private:
   Oper operfunc;
 
@@ -218,13 +212,9 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
-    addOperators();
-
     auto operatorID = cdo_operator_id();
     operfunc = (Oper) cdo_operator_f1(operatorID);
 
@@ -286,8 +276,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, &array1[0], &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, &array1[0], &numMissVals);
 
             auto is_EQ = dbl_is_equal;
             auto missval1 = varList1[varID].missval;
@@ -299,41 +289,41 @@ public:
                 // clang-format off
               switch (operfunc)
                 {
-                case Oper::Abs:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_abs); break;
-                case Oper::Int:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_int); break;
-                case Oper::Nint:  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_nint); break;
-                case Oper::Sqr:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_sqr); break;
+                case Oper::Abs:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_abs); break;
+                case Oper::Int:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_int); break;
+                case Oper::Nint:  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_nint); break;
+                case Oper::Sqr:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_sqr); break;
                 case Oper::Sqrt:
                   for (size_t i = 0; i < n; ++i) array2[i] = SQRTM(array1[i]);
                   break;
-                case Oper::Exp:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_exp); break;
-                case Oper::Ln:    check_lower_range(nmiss, n, missval1, array1, -1);
-                                  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_log); break;
-                case Oper::Log10: check_lower_range(nmiss, n, missval1, array1, -1);
-                                  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_log10); break;
-                case Oper::Sin:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_sin); break;
-                case Oper::Cos:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_cos); break;
-                case Oper::Tan:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_tan); break;
-                case Oper::Asin:  check_out_of_range(nmiss, n, missval1, array1, -1, 1);
-                                  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_asin); break;
-                case Oper::Acos:  check_out_of_range(nmiss, n, missval1, array1, -1, 1);
-                                  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_acos); break;
-                case Oper::Atan:  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_atan); break;
+                case Oper::Exp:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_exp); break;
+                case Oper::Ln:    check_lower_range(numMissVals, n, missval1, array1, -1);
+                                  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_log); break;
+                case Oper::Log10: check_lower_range(numMissVals, n, missval1, array1, -1);
+                                  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_log10); break;
+                case Oper::Sin:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_sin); break;
+                case Oper::Cos:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_cos); break;
+                case Oper::Tan:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_tan); break;
+                case Oper::Asin:  check_out_of_range(numMissVals, n, missval1, array1, -1, 1);
+                                  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_asin); break;
+                case Oper::Acos:  check_out_of_range(numMissVals, n, missval1, array1, -1, 1);
+                                  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_acos); break;
+                case Oper::Atan:  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_atan); break;
                 case Oper::Pow:
                   for (size_t i = 0; i < n; ++i) array2[i] = dbl_is_equal(array1[i], missval1) ? missval1 : std::pow(array1[i], rc);
                   break;
                 case Oper::Rand:
                   for (size_t i = 0; i < n; ++i) array2[i] = dbl_is_equal(array1[i], missval1) ? missval1 : ((double) std::rand()) / ((double) RAND_MAX);
                   break;
-                case Oper::Reci:  math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_reci); break;
-                case Oper::Not:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_not); break;
+                case Oper::Reci:  math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_reci); break;
+                case Oper::Not:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_not); break;
                 case Oper::Re:
-                case Oper::Arg:   math_varray_transform(nmiss, n, missval1, array1, array2, unary_op_nop); break;
+                case Oper::Arg:   math_varray_transform(numMissVals, n, missval1, array1, array2, unary_op_nop); break;
                 default: cdo_abort("Operator not implemented for real data!"); break;
                 }
                 // clang-format on
 
-                nmiss = varray_num_mv(n, array2, missval1);
+                numMissVals = varray_num_mv(n, array2, missval1);
               }
             else
               {
@@ -351,11 +341,11 @@ public:
                 }
                 // clang-format on
 
-                nmiss = 0;
+                numMissVals = 0;
               }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
@@ -369,18 +359,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Math(void *process)
-{
-  ModuleMath math;
-  math.init(process);
-  math.run();
-  math.close();
-
-  return nullptr;
-}
diff --git a/src/Merge.cc b/src/Merge.cc
index 4847295c0da1d3cc5f68efa1798f70499752ceb7..f6cefcd9d1411b0003237f7b17c37795973b63f4 100644
--- a/src/Merge.cc
+++ b/src/Merge.cc
@@ -113,8 +113,19 @@ cdo_inq_base_filetype(CdoStreamID streamID)
   return filetype;
 }
 
-class ModuleMerge
+class Merge : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Merge",
+    .operators = { { "merge", MergeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Merge> registration = RegisterEntry<Merge>(module);
 
   CdoStreamID streamID2;
   int nmerge;
@@ -137,9 +148,8 @@ class ModuleMerge
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -310,16 +320,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Merge(void *process)
-{
-  ModuleMerge merge;
-  merge.init(process);
-  merge.run();
-  merge.close();
-  return nullptr;
-}
diff --git a/src/Mergegrid.cc b/src/Mergegrid.cc
index 219621a4965712c666f257443d91c823049bf128..e287aae92b49edbfa567bd5b3d353c299ef84a28 100644
--- a/src/Mergegrid.cc
+++ b/src/Mergegrid.cc
@@ -18,8 +18,19 @@
 
 void genGridIndex(int gridID1, int gridID2, std::vector<long> &index);
 
-class ModuleMergegrid
+class Mergegrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Mergegrid",
+    .operators = { { "mergegrid", MergegridHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Mergegrid> registration = RegisterEntry<Mergegrid>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -40,9 +51,8 @@ class ModuleMergegrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -108,14 +118,14 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID2, &varID, &levelID);
-            size_t nmiss2;
-            cdo_read_record(streamID2, array2.data(), &nmiss2);
+            size_t numMissVals2;
+            cdo_read_record(streamID2, array2.data(), &numMissVals2);
 
             auto missval2 = vlistInqVarMissval(vlistID2, varID);
 
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss1;
-            cdo_read_record(streamID1, array1.data(), &nmiss1);
+            size_t numMissVals1;
+            cdo_read_record(streamID1, array1.data(), &numMissVals1);
 
             auto missval1 = vlistInqVarMissval(vlistID1, varID);
 
@@ -124,15 +134,15 @@ public:
                 if (gindex[i] >= 0 && !DBL_IS_EQUAL(array2[i], missval2)) { array1[gindex[i]] = array2[i]; }
               }
 
-            if (nmiss1)
+            if (numMissVals1)
               {
-                nmiss1 = 0;
+                numMissVals1 = 0;
                 for (size_t i = 0; i < gridsize1; ++i)
-                  if (DBL_IS_EQUAL(array1[i], missval1)) nmiss1++;
+                  if (DBL_IS_EQUAL(array1[i], missval1)) numMissVals1++;
               }
 
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, array1.data(), nmiss1);
+            cdo_write_record(streamID3, array1.data(), numMissVals1);
           }
 
         tsID++;
@@ -144,17 +154,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Mergegrid(void *process)
-{
-  ModuleMergegrid mergegrid;
-  mergegrid.init(process);
-  mergegrid.run();
-  mergegrid.close();
-  return nullptr;
-}
diff --git a/src/Mergetime.cc b/src/Mergetime.cc
index 15f219ee4f73aa1148bd5a606bd5be8b50e29563..df2163d7475f1b610c9b622182bbf59ffe70e52f 100644
--- a/src/Mergetime.cc
+++ b/src/Mergetime.cc
@@ -80,8 +80,19 @@ read_first_timestep(int nfiles, std::vector<StreamInfo> &streamInfo)
     }
 }
 
-class ModuleMergetime
+class Mergetime : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Mergetime",
+    .operators = { { "mergetime", MergeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Mergetime> registration = RegisterEntry<Mergetime>(module);
   int tsID2 = 0;
   int taxisID2 = CDI_UNDEFID;
   CdiDateTime lastDateTime{};
@@ -96,9 +107,8 @@ class ModuleMergetime
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -217,18 +227,5 @@ public:
   close()
   {
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Mergetime(void *process)
-{
-  ModuleMergetime mergetime;
-  mergetime.init(process);
-  mergetime.run();
-  mergetime.close();
-
-  return nullptr;
-}
diff --git a/src/Merstat.cc b/src/Merstat.cc
index 49bc12aa3cbf5f712cf4c72d8893a51ef5450c0a..19f38350bed79c78f41fca2dcd89d1d5676035da 100644
--- a/src/Merstat.cc
+++ b/src/Merstat.cc
@@ -28,29 +28,32 @@
 #include <mpim_grid.h>
 #include "field_functions.h"
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("merrange",  FieldFunc_Range,  0, nullptr);
-  cdo_operator_add("mermin",    FieldFunc_Min,    0, nullptr);
-  cdo_operator_add("mermax",    FieldFunc_Max,    0, nullptr);
-  cdo_operator_add("mersum",    FieldFunc_Sum,    0, nullptr);
-  cdo_operator_add("mermean",   FieldFunc_Meanw,  1, nullptr);
-  cdo_operator_add("meravg",    FieldFunc_Avgw,   1, nullptr);
-  cdo_operator_add("mervar",    FieldFunc_Varw,   1, nullptr);
-  cdo_operator_add("mervar1",   FieldFunc_Var1w,  1, nullptr);
-  cdo_operator_add("merstd",    FieldFunc_Stdw,   1, nullptr);
-  cdo_operator_add("merstd1",   FieldFunc_Std1w,  1, nullptr);
-  cdo_operator_add("merskew",   FieldFunc_Skew,   0, nullptr);
-  cdo_operator_add("merkurt",   FieldFunc_Kurt,   0, nullptr);
-  cdo_operator_add("mermedian", FieldFunc_Median, 0, nullptr);
-  cdo_operator_add("merpctl",   FieldFunc_Pctl,   0, nullptr);
-  // clang-format on
-}
-
-class ModuleMerstat
+class Merstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Merstat",
+    .operators = { { "merrange", FieldFunc_Range, 0, MerstatHelp },
+                   { "mermin", FieldFunc_Min, 0, MerstatHelp },
+                   { "mermax", FieldFunc_Max, 0, MerstatHelp },
+                   { "mersum", FieldFunc_Sum, 0, MerstatHelp },
+                   { "mermean", FieldFunc_Meanw, 1, MerstatHelp },
+                   { "meravg", FieldFunc_Avgw, 1, MerstatHelp },
+                   { "merstd", FieldFunc_Stdw, 1, MerstatHelp },
+                   { "merstd1", FieldFunc_Std1w, 1, MerstatHelp },
+                   { "mervar", FieldFunc_Varw, 1, MerstatHelp },
+                   { "mervar1", FieldFunc_Var1w, 1, MerstatHelp },
+                   { "merskew", FieldFunc_Skew, 0, MerstatHelp },
+                   { "merkurt", FieldFunc_Kurt, 0, MerstatHelp },
+                   { "mermedian", FieldFunc_Median, 0, MerstatHelp },
+                   { "merpctl", FieldFunc_Pctl, 0, MerstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Merstat> registration = RegisterEntry<Merstat>(module);
 
   int gridID1, gridID2 = -1, lastgrid = -1;
   int index;
@@ -71,11 +74,8 @@ class ModuleMerstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -182,18 +182,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Merstat(void *process)
-{
-  ModuleMerstat merstat;
-  merstat.init(process);
-  merstat.run();
-  merstat.close();
-
-  return nullptr;
-}
diff --git a/src/Monarith.cc b/src/Monarith.cc
index 81d6ee605ee1351f107a13ff7c738571549d91c7..c0e73b4b0a236ca025c5c9cafd0112d0932dc6ba 100644
--- a/src/Monarith.cc
+++ b/src/Monarith.cc
@@ -20,17 +20,22 @@
 #include "cdo_vlist.h"
 #include "field_functions.h"
 
-static void
-add_operators(void)
-{
-  cdo_operator_add("monadd", FieldFunc_Add, 0, nullptr);
-  cdo_operator_add("monsub", FieldFunc_Sub, 0, nullptr);
-  cdo_operator_add("monmul", FieldFunc_Mul, 0, nullptr);
-  cdo_operator_add("mondiv", FieldFunc_Div, 0, nullptr);
-}
-
-class ModuleMonarith
+class Monarith : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Monarith",
+    .operators = { { "monadd", FieldFunc_Add, 0, MonarithHelp },
+                   { "monsub", FieldFunc_Sub, 0, MonarithHelp },
+                   { "monmul", FieldFunc_Mul, 0, MonarithHelp },
+                   { "mondiv", FieldFunc_Div, 0, MonarithHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Monarith> registration = RegisterEntry<Monarith>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -47,11 +52,8 @@ class ModuleMonarith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -151,18 +153,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Monarith(void *process)
-{
-  ModuleMonarith monarith;
-  monarith.init(process);
-  monarith.run();
-  monarith.close();
-
-  return nullptr;
-}
diff --git a/src/Mrotuv.cc b/src/Mrotuv.cc
index 0e0f42db855e36be03ef094cd2cfc8e042235a33..e73a8f768478324960fa022ce4dd182778ee2bfe 100644
--- a/src/Mrotuv.cc
+++ b/src/Mrotuv.cc
@@ -176,9 +176,21 @@ p_to_uv_grid(long nlon, long nlat, Varray<double> &grid1x_v, Varray<double> &gri
       }
 }
 
-class ModuleMrotuv
+class Mrotuv : public Process
 {
-  size_t nmiss1 = 0, nmiss2 = 0;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Mrotuv",
+    .operators = { { "mrotuv"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 2, NoRestriction },
+  };
+  inline static RegisterEntry<Mrotuv> registration = RegisterEntry<Mrotuv>(module);
+
+  size_t numMissVals1 = 0, numMissVals2 = 0;
   int uid = -1, vid = -1;
 
   CdoStreamID streamID1;
@@ -212,10 +224,8 @@ class ModuleMrotuv
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -322,6 +332,7 @@ public:
     missval1 = vlistInqVarMissval(vlistID1, uid);
     missval2 = vlistInqVarMissval(vlistID1, vid);
   }
+
   void
   run()
   {
@@ -356,14 +367,14 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            if (varID == uid) cdo_read_record(streamID1, urfield[levelID].data(), &nmiss1);
-            if (varID == vid) cdo_read_record(streamID1, vrfield[levelID].data(), &nmiss2);
+            if (varID == uid) cdo_read_record(streamID1, urfield[levelID].data(), &numMissVals1);
+            if (varID == vid) cdo_read_record(streamID1, vrfield[levelID].data(), &numMissVals2);
           }
 
         for (int levelID = 0; levelID < nlevs; ++levelID)
           {
             // remove missing values
-            if (nmiss1 || nmiss2)
+            if (numMissVals1 || numMissVals2)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
@@ -402,33 +413,20 @@ public:
             for (size_t i = 0; i < nlon; ++i) { vfield[nlat - 1][i] = vhelp[nlat - 1][i + 1]; }
 
             cdo_def_record(streamID2, 0, levelID);
-            cdo_write_record(streamID2, ufield_v.data(), nmiss1);
+            cdo_write_record(streamID2, ufield_v.data(), numMissVals1);
             cdo_def_record(streamID3, 0, levelID);
-            cdo_write_record(streamID3, vfield_v.data(), nmiss2);
+            cdo_write_record(streamID3, vfield_v.data(), numMissVals2);
           }
 
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Mrotuv(void *process)
-{
-  ModuleMrotuv mrotuv;
-
-  mrotuv.init(process);
-  mrotuv.run();
-  mrotuv.close();
-
-  return nullptr;
-}
diff --git a/src/Mrotuvb.cc b/src/Mrotuvb.cc
index 605290acb8af4644c21214a548238a8061016fc8..003eefd7c979734011d0b710f9c6899f87257f4d 100644
--- a/src/Mrotuvb.cc
+++ b/src/Mrotuvb.cc
@@ -225,8 +225,19 @@ uv_to_p_grid(size_t nlon, size_t nlat, Varray<double> &grid1x_v, Varray<double>
       }
 }
 
-class ModuleMrotuvb
+class Mrotuvb : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Mrotuvb",
+    .operators = { { "mrotuvb", MrotuvbHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Mrotuvb> registration = RegisterEntry<Mrotuvb>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -254,9 +265,8 @@ class ModuleMrotuvb
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     gpint = !(cdo_operator_argc() == 1 && cdo_operator_argv(0) == "noint");
 
@@ -396,12 +406,12 @@ public:
             cdo_inq_record(streamID1, &varID1, &levelID);
             cdo_inq_record(streamID2, &varID2, &levelID);
 
-            size_t nmiss1, nmiss2;
-            cdo_read_record(streamID1, ufield_v.data(), &nmiss1);
-            cdo_read_record(streamID2, vfield_v.data(), &nmiss2);
+            size_t numMissVals1, numMissVals2;
+            cdo_read_record(streamID1, ufield_v.data(), &numMissVals1);
+            cdo_read_record(streamID2, vfield_v.data(), &numMissVals2);
 
             // remove missing values
-            if (nmiss1 || nmiss2)
+            if (numMissVals1 || numMissVals2)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
@@ -483,18 +493,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Mrotuvb(void *process)
-{
-  ModuleMrotuvb mrotuvb;
-  mrotuvb.init(process);
-  mrotuvb.run();
-  mrotuvb.close();
-
-  return nullptr;
-}
diff --git a/src/NCL_wind.cc b/src/NCL_wind.cc
index e30942db146242dbefaf5cf1f27e105a2762caab..07bd3e05b54aea432dbeb917703268fc07457c4e 100644
--- a/src/NCL_wind.cc
+++ b/src/NCL_wind.cc
@@ -36,7 +36,7 @@ uv2dv_cfd_W(double missval, double *u, double *v, double *lon, double *lat, size
       double *tmp_v = v + k * gridsize_uv;
       double *tmp_div = div + k * gridsize_uv;
       // Init output array.
-      varray_fill(gridsize_uv, tmp_div, 0.0);
+      ranges::fill_n(tmp_div, gridsize_uv, 0.0);
       // Call the Fortran routine.
 #ifdef HAVE_CF_INTERFACE
       DDVFIDF(tmp_u, tmp_v, lat, lon, inlon, inlat, missval, boundOpt, tmp_div, ierror);
@@ -66,7 +66,7 @@ uv2vr_cfd_W(double missval, double *u, double *v, double *lon, double *lat, size
       double *tmp_v = v + k * gridsize_uv;
       double *tmp_vort = vort + k * gridsize_uv;
       // Init output array.
-      varray_fill(gridsize_uv, tmp_vort, 0.0);
+      ranges::fill_n(tmp_vort, gridsize_uv, 0.0);
       // Call the Fortran routine.
 #ifdef HAVE_CF_INTERFACE
       DVRFIDF(tmp_u, tmp_v, lat, lon, inlon, inlat, missval, boundOpt, tmp_vort, ierror);
@@ -96,10 +96,10 @@ enum struct OutMode
 };
 
 // Parameter
-OutMode outMode(OutMode::NEW);
-int boundOpt = -1;
-std::string uName;
-std::string vName;
+static OutMode outMode(OutMode::NEW);
+static int boundOpt = -1;
+static std::string uName;
+static std::string vName;
 
 static void
 print_parameter(void)
@@ -123,7 +123,7 @@ set_parameter(void)
 
       KVList kvlist;
       kvlist.name = "PARAMETER";
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -152,8 +152,24 @@ set_parameter(void)
   if (Options::cdoVerbose) print_parameter();
 }
 
-class ModuleNCL_wind
+class NCL_wind : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "NCL_wind",
+    // clang-format off
+    .operators = { { "uv2dv_cfd", 0, 0, "[u,v,boundsOpt,outMode]", NCL_windHelp },
+                   { "uv2vr_cfd", 0, 0, "[u,v,boundsOpt,outMode]", NCL_windHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<NCL_wind> registration = RegisterEntry<NCL_wind>(module);
+
+  int UV2DV_CFD, UV2VR_CFD;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -182,17 +198,14 @@ class ModuleNCL_wind
   Varray<double> lon;
   Varray<double> lat;
 
-  int UV2DV_CFD, UV2VR_CFD;
   VarList varList1;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    UV2DV_CFD = cdo_operator_add("uv2dv_cfd", 0, 0, "[u, v, boundsOpt, outMode]");
-    UV2VR_CFD = cdo_operator_add("uv2vr_cfd", 0, 0, "[u, v, boundsOpt, outMode]");
+    UV2DV_CFD = module.get_id("uv2dv_cfd");
+    UV2VR_CFD = module.get_id("uv2vr_cfd");
 
     operatorID = cdo_operator_id();
 
@@ -281,7 +294,6 @@ public:
   void
   run()
   {
-
     const auto &varU = varList1[varIDu];
     const auto &varV = varList1[varIDv];
     int tsID = 0;
@@ -290,7 +302,7 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        size_t nmissu = 0, nmissv = 0;
+        size_t numMissValsu = 0, numMissValsv = 0;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
@@ -299,34 +311,34 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, &array[0], &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, &array[0], &numMissVals);
 
             if (varID == varIDu || varID == varIDv)
               {
                 if (varID == varIDu)
                   {
                     std::copy_n(&array[0], gridsizeuv, &arrayu[levelID * gridsizeuv]);
-                    nmissu += nmiss;
+                    numMissValsu += numMissVals;
                   }
                 if (varID == varIDv)
                   {
                     std::copy_n(&array[0], gridsizeuv, &arrayv[levelID * gridsizeuv]);
-                    nmissv += nmiss;
+                    numMissValsv += numMissVals;
                   }
               }
 
             if (outMode == OutMode::APPEND)
               {
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, &array[0], nmiss);
+                cdo_write_record(streamID2, &array[0], numMissVals);
               }
           }
 
-        if (nmissu != nmissv)
+        if (numMissValsu != numMissValsv)
           {
             cdo_abort("u and v have different number of missing values!");
-            if (nmissu && !dbl_is_equal(varU.missval, varV.missval))
+            if (numMissValsu && !dbl_is_equal(varU.missval, varV.missval))
               {
                 for (int levelID = 0; levelID < nlev; ++levelID)
                   {
@@ -345,14 +357,15 @@ public:
         for (int levelID = 0; levelID < nlev; ++levelID)
           {
             auto parray = &arrayo[levelID * gridsizeuv];
-            auto nmiss = array_num_mv(gridsizeuv, parray, varU.missval);
+            auto numMissVals = array_num_mv(gridsizeuv, parray, varU.missval);
             cdo_def_record(streamID2, varIDo, levelID);
-            cdo_write_record(streamID2, parray, nmiss);
+            cdo_write_record(streamID2, parray, numMissVals);
           }
 
         tsID++;
       }
   }
+
   void
   close()
   {
@@ -360,19 +373,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-NCL_wind(void *process)
-
-{
-  ModuleNCL_wind ncl_wind;
-
-  ncl_wind.init(process);
-  ncl_wind.run();
-  ncl_wind.close();
-
-  return nullptr;
-}
diff --git a/src/Ninfo.cc b/src/Ninfo.cc
index 5e4c5ce9608afdb4de99519c4317c0820dd5b6eb..df57a367de3c4a05d71b50a2fd5bfde0ae964882 100644
--- a/src/Ninfo.cc
+++ b/src/Ninfo.cc
@@ -22,7 +22,7 @@
 
 #include "process_int.h"
 
-class ModuleNinfo
+class Ninfo : public Process
 {
   enum
   {
@@ -36,6 +36,26 @@ class ModuleNinfo
     NGRIDS
   };
 
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ninfo",
+    .operators = { { "nyear", NYEAR, 0, NinfoHelp },
+                   { "nmon", NMON, 0, NinfoHelp },
+                   { "ndate", NDATE, 0, NinfoHelp },
+                   { "ntime", NTIME, 0, NinfoHelp },
+                   { "ncode", NinfoHelp },
+                   { "npar", NPAR, 0, NinfoHelp },
+                   { "nlevel", NLEVEL, 0, NinfoHelp },
+                   { "ngridpoints", NGRIDPOINTS, 0, NinfoHelp },
+                   { "ngrids", NGRIDS, 0, NinfoHelp } },
+    .aliases = { { "nvar", "npar" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Ninfo> registration = RegisterEntry<Ninfo>(module);
+
   int operfunc;
   int ntsteps;
   CdoStreamID streamID;
@@ -47,18 +67,8 @@ class ModuleNinfo
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("nyear", NYEAR, 0, nullptr);
-    cdo_operator_add("nmon", NMON, 0, nullptr);
-    cdo_operator_add("ndate", NDATE, 0, nullptr);
-    cdo_operator_add("ntime", NTIME, 0, nullptr);
-    cdo_operator_add("npar", NPAR, 0, nullptr);
-    cdo_operator_add("nlevel", NLEVEL, 0, nullptr);
-    cdo_operator_add("ngridpoints", NGRIDPOINTS, 0, nullptr);
-    cdo_operator_add("ngrids", NGRIDS, 0, nullptr);
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -165,17 +175,5 @@ public:
   close()
   {
     cdo_stream_close(streamID);
-
-    cdo_finish();
   }
 };
-
-void *
-Ninfo(void *process)
-{
-  ModuleNinfo ninfo;
-  ninfo.init(process);
-  ninfo.run();
-  ninfo.close();
-  return nullptr;
-}
diff --git a/src/Nmldump.cc b/src/Nmldump.cc
index fc068039a4517fa1103a940a8aa7c1d09355a5f5..93d5e8e0dcf6a8dae433a1b0c8e7dddce01176fe 100644
--- a/src/Nmldump.cc
+++ b/src/Nmldump.cc
@@ -65,20 +65,30 @@ kvldump(const PMList &pmlist)
     }
 }
 
-class ModuleNmldump
+class Nmldump : public Process
 {
-  int operatorID;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Nmldump",
+    .operators = { { "nmldump"}, { "kvldump"} },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Nmldump> registration = RegisterEntry<Nmldump>(module);
   int NMLDUMP, KVLDUMP;
+  int operatorID;
   PMList pmlist;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    NMLDUMP = cdo_operator_add("nmldump", 0, 0, nullptr);
-    KVLDUMP = cdo_operator_add("kvldump", 0, 0, nullptr);
+    NMLDUMP = module.get_id("nmldump");
+    KVLDUMP = module.get_id("kvldump");
 
     operatorID = cdo_operator_id();
 
@@ -98,18 +108,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Nmldump(void *process)
-{
-  ModuleNmldump nmldump;
-
-  nmldump.init(process);
-  nmldump.run();
-  nmldump.close();
-
-  return nullptr;
-}
diff --git a/src/Output.cc b/src/Output.cc
index 30402d50fd0dcba0222f4d683964fa9dcb51eebd..0a3d924626a97d6736a6a60c166901ea7c29a27f 100644
--- a/src/Output.cc
+++ b/src/Output.cc
@@ -225,9 +225,59 @@ outputf(int nelem, const std::string &format, size_t gridsize, const Varray<doub
   fprintf(stdout, "\n");
 }
 
-class ModuleOutput
+class Output : public Process
 {
-  size_t nmiss;
+  enum
+  {
+    knohead,
+    kvalue,
+    kparam,
+    kcode,
+    kname,
+    kx,
+    ky,
+    klon,
+    klat,
+    klev,
+    kbin,
+    kxind,
+    kyind,
+    ktimestep,
+    kdate,
+    ktime,
+    kyear,
+    kmonth,
+    kday
+  };
+  struct KeyLenEntry
+  {
+    std::string key;
+    int idx;
+    int len;
+  };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Output",
+    .operators = { { "output", 0, 1, OutputHelp },
+                   { "outputint", OutputHelp },
+                   { "outputsrv", OutputHelp },
+                   { "outputext", OutputHelp },
+                   { "outputf", OutputHelp },
+                   { "outputts", OutputHelp },
+                   { "outputfld", OutputHelp },
+                   { "outputarr", OutputHelp },
+                   { "outputxyz", OutputHelp },
+                   { "outputtab", OutputtabHelp } },
+    .aliases = { { "outputkey", "outputtab" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { -1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Output> registration = RegisterEntry<Output>(module);
+  int OUTPUT, OUTPUTINT, OUTPUTSRV, OUTPUTEXT, OUTPUTF, OUTPUTTS, OUTPUTFLD, OUTPUTARR, OUTPUTXYZ, OUTPUTTAB;
+  size_t numMissVals;
   int nelem = 1;
   int index;
   std::string format;
@@ -238,12 +288,6 @@ class ModuleOutput
   int operatorID;
 
   // clang-format off
-  enum {knohead,   kvalue,  kparam,  kcode,  kname,  kx,  ky,  klon,  klat,  klev,  kbin,  kxind,  kyind,  ktimestep,  kdate,  ktime,  kyear,  kmonth,  kday};
-  struct KeyLenEntry {
-    std::string key;
-    int         idx;
-    int         len;
-  };
   std::vector<KeyLenEntry> keyMap = {
     { "nohead",   knohead,    0 },
     { "value",    kvalue,     8 },
@@ -267,25 +311,21 @@ class ModuleOutput
     // clang-format on
   };
 
-  int OUTPUT, OUTPUTINT, OUTPUTSRV, OUTPUTEXT, OUTPUTF, OUTPUTTS, OUTPUTFLD, OUTPUTARR, OUTPUTXYZ, OUTPUTTAB;
-
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
-    OUTPUT = cdo_operator_add("output", 0, 1, nullptr);
-    OUTPUTINT = cdo_operator_add("outputint", 0, 0, nullptr);
-    OUTPUTSRV = cdo_operator_add("outputsrv", 0, 0, nullptr);
-    OUTPUTEXT = cdo_operator_add("outputext", 0, 0, nullptr);
-    OUTPUTF = cdo_operator_add("outputf", 0, 0, nullptr);
-    OUTPUTTS = cdo_operator_add("outputts", 0, 0, nullptr);
-    OUTPUTFLD = cdo_operator_add("outputfld", 0, 0, nullptr);
-    OUTPUTARR = cdo_operator_add("outputarr", 0, 0, nullptr);
-    OUTPUTXYZ = cdo_operator_add("outputxyz", 0, 0, nullptr);
-    OUTPUTTAB = cdo_operator_add("outputtab", 0, 0, nullptr);
+    OUTPUT = module.get_id("output");
+    OUTPUTINT = module.get_id("outputint");
+    OUTPUTSRV = module.get_id("outputsrv");
+    OUTPUTEXT = module.get_id("outputext");
+    OUTPUTF = module.get_id("outputf");
+    OUTPUTTS = module.get_id("outputts");
+    OUTPUTFLD = module.get_id("outputfld");
+    OUTPUTARR = module.get_id("outputarr");
+    OUTPUTXYZ = module.get_id("outputxyz");
+    OUTPUTTAB = module.get_id("outputtab");
 
     (void) (OUTPUT);  // unused
 
@@ -427,7 +467,7 @@ public:
                     nlat = 1;
                   }
 
-                cdo_read_record(streamID, array.data(), &nmiss);
+                cdo_read_record(streamID, array.data(), &numMissVals);
 
                 auto vdate = cdiDate_get(vDateTime.date);
                 auto vtime = cdiTime_get(vDateTime.time);
@@ -525,16 +565,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Output(void *process)
-{
-  ModuleOutput output;
-  output.init(process);
-  output.run();
-  output.close();
-  return nullptr;
-}
diff --git a/src/Outputgmt.cc b/src/Outputgmt.cc
index 5de0cdf7d03f396b209a7c507337e519ad475054..5447e7f6f2beaf07f64ed40af86a6eba71575dd3 100644
--- a/src/Outputgmt.cc
+++ b/src/Outputgmt.cc
@@ -353,11 +353,31 @@ output_vector(long nlon, long nlat, int ninc, const Varray<double> &lon, const V
   fprintf(stdout, "#\n");
 }
 
-class ModuleOutputgmt
+class Outputgmt : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Outputgmt",
+    .operators = { { "gmtxyz", OutputgmtHelp },
+                   { "gmtcells", OutputgmtHelp },
+                   { "outputcenter2", OutputgmtHelp },
+                   { "outputcentercpt", OutputgmtHelp },
+                   { "outputboundscpt", OutputgmtHelp },
+                   { "outputvector", OutputgmtHelp },
+                   { "outputtri", OutputgmtHelp },
+                   { "outputvrml", OutputgmtHelp },
+                   { "outputkml", OutputgmtHelp } },
+    .aliases = { { "outputcenter", "gmtxyz" }, { "outputbounds", "gmtcells" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Outputgmt> registration = RegisterEntry<Outputgmt>(module);
+  int GMTXYZ, OUTPUTCENTER2, OUTPUTCENTERCPT, GMTCELLS, OUTPUTBOUNDSCPT, OUTPUTVECTOR, OUTPUTTRI, OUTPUTVRML, OUTPUTKML;
   int varID0;
   size_t gridsize2 = 0;
-  size_t nmiss;
+  size_t numMissVals;
   int ninc = 1;
   bool lzon = false, lmer = false, lhov = false;
   Varray<double> grid_center_lat2, grid_center_lon2;
@@ -386,8 +406,6 @@ class ModuleOutputgmt
   bool printHeader;
   bool grid_is_circular;
 
-  int GMTXYZ, OUTPUTCENTER2, OUTPUTCENTERCPT, GMTCELLS, OUTPUTBOUNDSCPT, OUTPUTVECTOR, OUTPUTTRI, OUTPUTVRML, OUTPUTKML;
-
   VarList varList;
   Varray<double> array;
   Varray<double> array2;
@@ -406,19 +424,18 @@ class ModuleOutputgmt
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    GMTXYZ = cdo_operator_add("gmtxyz", 0, 0, nullptr);
-    OUTPUTCENTER2 = cdo_operator_add("outputcenter2", 0, 0, nullptr);
-    OUTPUTCENTERCPT = cdo_operator_add("outputcentercpt", 0, 0, nullptr);
-    GMTCELLS = cdo_operator_add("gmtcells", 0, 0, nullptr);
-    OUTPUTBOUNDSCPT = cdo_operator_add("outputboundscpt", 0, 0, nullptr);
-    OUTPUTVECTOR = cdo_operator_add("outputvector", 0, 0, nullptr);
-    OUTPUTTRI = cdo_operator_add("outputtri", 0, 0, nullptr);
-    OUTPUTVRML = cdo_operator_add("outputvrml", 0, 0, nullptr);
-    OUTPUTKML = cdo_operator_add("outputkml", 0, 0, nullptr);
+
+    GMTXYZ = module.get_id("gmtxyz");
+    OUTPUTCENTER2 = module.get_id("outputcenter2");
+    OUTPUTCENTERCPT = module.get_id("outputcentercpt");
+    GMTCELLS = module.get_id("gmtcells");
+    OUTPUTBOUNDSCPT = module.get_id("outputboundscpt");
+    OUTPUTVECTOR = module.get_id("outputvector");
+    OUTPUTTRI = module.get_id("outputtri");
+    OUTPUTVRML = module.get_id("outputvrml");
+    OUTPUTKML = module.get_id("outputkml");
 
     operatorID = cdo_operator_id();
 
@@ -636,7 +653,7 @@ public:
             if (varID != varID0) continue;
             if (recID > 0 && !lzon && !lmer) continue;
 
-            cdo_read_record(streamID, array.data(), &nmiss);
+            cdo_read_record(streamID, array.data(), &numMissVals);
 
             if (operatorID == OUTPUTCENTER2 && grid_is_circular) make_cyclic(array.data(), array2.data(), nlon, nlat);
 
@@ -717,7 +734,7 @@ public:
 
                 varray_copy(gridsize, array, uf);
                 cdo_inq_record(streamID, &varID, &levelID);
-                cdo_read_record(streamID, vf.data(), &nmiss);
+                cdo_read_record(streamID, vf.data(), &numMissVals);
 
                 output_vector(nlon, nlat, ninc, grid_center_lon, grid_center_lat, uf, vf);
 
@@ -782,17 +799,5 @@ public:
   close()
   {
     cdo_stream_close(streamID);
-
-    cdo_finish();
   }
 };
-
-void *
-Outputgmt(void *process)
-{
-  ModuleOutputgmt outputgmt;
-  outputgmt.init(process);
-  outputgmt.run();
-  outputgmt.close();
-  return nullptr;
-}
diff --git a/src/Pack.cc b/src/Pack.cc
index 14bba9954498050e933adb963ae6c582d2496f08..59549835cba1f5586905a9f4416a6431322bc35c 100644
--- a/src/Pack.cc
+++ b/src/Pack.cc
@@ -11,11 +11,8 @@
       Pack    pack         Pack
 */
 
-#ifdef _OPENMP
-#include <omp.h>
-#endif
-
 #include <climits>
+#include <fstream>
 
 #include <cdi.h>
 
@@ -24,6 +21,68 @@
 #include "datetime.h"
 #include "cdo_default_values.h"
 #include "field_functions.h"
+#include "pmlist.h"
+#include "param_conversion.h"
+#include "util_string.h"
+
+struct PackParams
+{
+  double add_offset{ 0 };
+  double scale_factor{ 1 };
+  std::string filename;
+  bool printParam{ false };
+};
+
+struct PackEntry
+{
+  double add_offset{ 0 };
+  double scale_factor{ 1 };
+  std::string name;
+};
+
+static std::vector<PackEntry>
+read_params_from_file(const std::string &filename)
+{
+  std::vector<PackEntry> packList;
+
+  if (filename.size())
+    {
+      std::ifstream file(filename);
+      if (!file.is_open()) cdo_abort("Open failed on: %s\n", filename);
+
+      std::string line;
+      while (std::getline(file, line))
+        {
+          auto keyValuesLine = split_string(line, " +");
+
+          KVList kvlist;
+          if (kvlist.parse_arguments(keyValuesLine) != 0) cdo_abort("Parse error!");
+          if (Options::cdoVerbose) kvlist.print();
+
+          PackEntry packEntry;
+          for (const auto &kv : kvlist)
+            {
+              const auto &key = kv.key;
+              if (kv.nvalues > 1) cdo_abort("Too many values for parameter key >%s<!", key);
+              if (kv.nvalues < 1) cdo_abort("Missing value for parameter key >%s<!", key);
+              const auto &value = kv.values[0];
+
+              // clang-format off
+              if      (key == "name")         packEntry.name = parameter_to_word(value);
+              else if (key == "add_offset")   packEntry.add_offset = parameter_to_double(value);
+              else if (key == "scale_factor") packEntry.scale_factor = parameter_to_double(value);
+              else cdo_abort("Invalid parameter key >%s<!", key);
+              // clang-format on
+            }
+
+          if (!packEntry.name.empty()) packList.push_back(packEntry);
+        }
+
+      file.close();
+    }
+
+  return packList;
+}
 
 static int
 get_type_values(int datatype, double &tmin, double &tmax, double &tmv)
@@ -67,14 +126,14 @@ compute_scale_and_offset(int datatype, double fmin, double fmax, double &scaleFa
 static MinMax
 field_min_max(Field &field)
 {
-  auto nmiss = field.nmiss;
+  auto numMissVals = field.numMissVals;
   auto missval = field.missval;
   auto len = field.size;
 
   if (field.memType == MemType::Float)
-    return nmiss ? varray_min_max_mv(len, field.vec_f, (float) missval) : varray_min_max(len, field.vec_f);
+    return numMissVals ? varray_min_max_mv(len, field.vec_f, (float) missval) : varray_min_max(len, field.vec_f);
   else
-    return nmiss ? varray_min_max_mv(len, field.vec_d, missval) : varray_min_max(len, field.vec_d);
+    return numMissVals ? varray_min_max_mv(len, field.vec_d, missval) : varray_min_max(len, field.vec_d);
 }
 
 static void
@@ -96,8 +155,74 @@ field_change_missval(Field &field, double missval1, double missval2)
     }
 }
 
-class ModulePack
+static void
+print_parameter(const PackParams &params)
 {
+  std::stringstream outbuffer;
+
+  outbuffer << "add_offset=" << params.add_offset;
+  outbuffer << ", scale_factor=" << params.scale_factor;
+  outbuffer << ", printpack=" << params.printParam;
+  outbuffer << ", filename=" << params.filename;
+
+  cdo_verbose("%s", outbuffer.str());
+}
+
+static void
+print_pack_params(const std::string& name, double addOffset, double scaleFactor)
+{
+  fprintf(stdout, "name=%s  add_offset=%.9g  scale_factor=%.9g\n", name.c_str(), addOffset, scaleFactor);
+}
+
+static PackParams
+get_parameter()
+{
+  PackParams params;
+
+  auto pargc = cdo_operator_argc();
+  if (pargc)
+    {
+      const auto &pargv = cdo_get_oper_argv();
+
+      KVList kvlist;
+      kvlist.name = cdo_module_name();
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
+      if (Options::cdoVerbose) kvlist.print();
+
+      for (const auto &kv : kvlist)
+        {
+          const auto &key = kv.key;
+          if (kv.nvalues > 1) cdo_abort("Too many values for parameter key >%s<!", key);
+          if (kv.nvalues < 1) cdo_abort("Missing value for parameter key >%s<!", key);
+          const auto &value = kv.values[0];
+
+          // clang-format off
+          if      (key == "add_offset")   params.add_offset = parameter_to_double(value);
+          else if (key == "scale_factor") params.scale_factor = parameter_to_double(value);
+          else if (key == "printparam")   params.printParam = parameter_to_bool(value);
+          else if (key == "filename")     params.filename = parameter_to_word(value);
+          else cdo_abort("Invalid parameter key >%s<!", key);
+          // clang-format on
+        }
+    }
+
+  return params;
+}
+
+class Pack : public Process
+{
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Pack",
+    .operators = { { "pack", PackHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Pack>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -107,52 +232,20 @@ class ModulePack
   int vlistID1;
   int vlistID2;
 
+  PackParams params;
+
   int datatype = CDI_DATATYPE_INT16;
   int nvars;
 
   VarList varList;
-  FieldVector3D vars;
-  DateTimeList dtlist;
 
-public:
+private:
   void
-  init(void *process)
+  run_method1()
   {
-    cdo_initialize(process);
-
-    operator_check_argc(0);
-
-    streamID1 = cdo_open_read(0);
-
-    vlistID1 = cdo_stream_inq_vlist(streamID1);
-    vlistID2 = vlistDuplicate(vlistID1);
-
-    taxisID1 = vlistInqTaxis(vlistID1);
-    taxisID2 = taxisDuplicate(taxisID1);
-    vlistDefTaxis(vlistID2, taxisID2);
-
-    streamID2 = cdo_open_write(1);
+    FieldVector3D vars;
+    DateTimeList dtlist;
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
-
-    if (CdoDefault::DataType != CDI_UNDEFID)
-      {
-        // 32-bit float routing error with CDI_DATATYPE_INT32|CDI_DATATYPE_UINT32
-        if (CdoDefault::DataType == CDI_DATATYPE_FLT64 || CdoDefault::DataType == CDI_DATATYPE_FLT32 ||
-            CdoDefault::DataType == CDI_DATATYPE_INT32 || CdoDefault::DataType == CDI_DATATYPE_UINT32)
-          {
-            cdo_warning("Changed default output datatype to int16");
-            CdoDefault::DataType = datatype;
-          }
-        else { datatype = CdoDefault::DataType; }
-      }
-  }
-
-  void
-  run()
-  {
     int tsID = 0;
     while (true)
       {
@@ -187,7 +280,7 @@ public:
         const auto &var = varList[varID];
 
         double fmin = undefValue, fmax = -undefValue;
-        size_t nmisspv = 0;
+        size_t numMissValspv = 0;
 
         for (int levelID = 0; levelID < var.nlevels; ++levelID)
           {
@@ -196,11 +289,11 @@ public:
                 if (t > 0 && var.isConstant) continue;
 
                 auto &field = vars[t][varID][levelID];
-                auto nmiss = field.nmiss;
+                auto numMissVals = field.numMissVals;
 
-                if (nmiss) nmisspv += nmiss;
+                if (numMissVals) numMissValspv += numMissVals;
 
-                if (nmiss < var.gridsize)
+                if (numMissVals < var.gridsize)
                   {
                     auto mm = field_min_max(field);
                     fmin = std::min(fmin, mm.min);
@@ -213,7 +306,7 @@ public:
 
         auto hasValidData = (is_not_equal(fmin, undefValue) && is_not_equal(fmax, -undefValue));
 
-        if (nmisspv)
+        if (numMissValspv)
           {
             double tmin, tmax, missval2;
             if (!get_type_values(datatype, tmin, tmax, missval2))
@@ -230,7 +323,7 @@ public:
                         if (t > 0 && var.isConstant) continue;
 
                         auto &field = vars[t][varID][levelID];
-                        if (field.nmiss) field_change_missval(field, var.missval, missval2);
+                        if (field.numMissVals) field_change_missval(field, var.missval, missval2);
                       }
                   }
               }
@@ -244,6 +337,8 @@ public:
                 auto memTypeIsFloat = (var.memType == MemType::Float);
                 cdiDefKeyFloat(vlistID2, varID, CDI_KEY_ADDOFFSET, memTypeIsFloat ? (float) addOffset : addOffset);
                 cdiDefKeyFloat(vlistID2, varID, CDI_KEY_SCALEFACTOR, memTypeIsFloat ? (float) scaleFactor : scaleFactor);
+
+                if (params.printParam) print_pack_params(var.name, addOffset, scaleFactor);
               }
           }
       }
@@ -272,6 +367,106 @@ public:
       }
   }
 
+  void
+  run_method2()
+  {
+    const auto packList = read_params_from_file(params.filename);
+
+    for (int varID = 0; varID < nvars; ++varID)
+      {
+        const auto &var = varList[varID];
+
+        for (auto &packEntry: packList)
+          {
+            if (var.name == packEntry.name)
+              {
+                vlistDefVarDatatype(vlistID2, varID, datatype);
+
+                auto scaleFactor = packEntry.scale_factor;
+                auto addOffset = packEntry.add_offset;
+                auto memTypeIsFloat = (var.memType == MemType::Float);
+                cdiDefKeyFloat(vlistID2, varID, CDI_KEY_ADDOFFSET, memTypeIsFloat ? (float) addOffset : addOffset);
+                cdiDefKeyFloat(vlistID2, varID, CDI_KEY_SCALEFACTOR, memTypeIsFloat ? (float) scaleFactor : scaleFactor);
+
+                if (params.printParam) print_pack_params(var.name, addOffset, scaleFactor);
+
+                break;
+              }
+          }
+      }
+
+    cdo_def_vlist(streamID2, vlistID2);
+
+    Field field;
+
+    int tsID = 0;
+    while (true)
+      {
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        if (nrecs == 0) break;
+
+        cdo_taxis_copy_timestep(taxisID2, taxisID1);
+        cdo_def_timestep(streamID2, tsID);
+
+        for (int recID = 0; recID < nrecs; ++recID)
+          {
+            int varID, levelID;
+            cdo_inq_record(streamID1, &varID, &levelID);
+            field.init(varList[varID]);
+            cdo_read_record(streamID1, field);
+
+            cdo_def_record(streamID2, varID, levelID);
+            cdo_write_record(streamID2, field);
+          }
+
+        tsID++;
+      }
+  }
+
+public:
+  void
+  init()
+  {
+    params = get_parameter();
+    if (Options::cdoVerbose) print_parameter(params);
+
+    streamID1 = cdo_open_read(0);
+
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
+    vlistID2 = vlistDuplicate(vlistID1);
+
+    taxisID1 = vlistInqTaxis(vlistID1);
+    taxisID2 = taxisDuplicate(taxisID1);
+    vlistDefTaxis(vlistID2, taxisID2);
+
+    streamID2 = cdo_open_write(1);
+
+    varList_init(varList, vlistID1);
+
+    nvars = vlistNvars(vlistID1);
+
+    if (CdoDefault::DataType != CDI_UNDEFID)
+      {
+        // 32-bit float rounding error with CDI_DATATYPE_INT32|CDI_DATATYPE_UINT32
+        if (CdoDefault::DataType == CDI_DATATYPE_FLT64 || CdoDefault::DataType == CDI_DATATYPE_FLT32
+            || CdoDefault::DataType == CDI_DATATYPE_INT32 || CdoDefault::DataType == CDI_DATATYPE_UINT32)
+          {
+            cdo_warning("Changed default output datatype to int16");
+            CdoDefault::DataType = datatype;
+          }
+        else { datatype = CdoDefault::DataType; }
+      }
+  }
+
+  void
+  run()
+  {
+    if (params.filename.empty())
+      run_method1();
+    else
+      run_method2();
+  }
+
   void
   close()
   {
@@ -279,18 +474,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Pack(void *process)
-{
-  ModulePack pack;
-  pack.init(process);
-  pack.run();
-  pack.close();
-
-  return nullptr;
-}
diff --git a/src/Pardup.cc b/src/Pardup.cc
index 6de1cd2944e32a680c733e2b57d32964be760448..1187cdd2640feebb5478ce4fdabf1e60317575d6 100644
--- a/src/Pardup.cc
+++ b/src/Pardup.cc
@@ -18,8 +18,20 @@
 #include "process_int.h"
 #include "param_conversion.h"
 
-class ModulePardup
+class Pardup : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Pardup",
+    .operators = { { "pardup"}, { "parmul"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Pardup> registration = RegisterEntry<Pardup>(module);
+  int PARDUP, PARMUL;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -32,16 +44,15 @@ class ModulePardup
   std::vector<RecordInfo> recList;
   Varray<double> array;
   Varray2D<double> vardata;
-  std::vector<std::vector<size_t>> varnmiss;
+  std::vector<std::vector<size_t>> varnumMissVals;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    const auto PARDUP = cdo_operator_add("pardup", 0, 0, nullptr);
-    const auto PARMUL = cdo_operator_add("parmul", 0, 0, nullptr);
+    PARDUP = module.get_id("pardup");
+    PARMUL = module.get_id("parmul");
 
     const auto operatorID = cdo_operator_id();
 
@@ -73,14 +84,14 @@ public:
     const auto gridsizemax = vlistGridsizeMax(vlistID1);
     array = Varray<double>(gridsizemax);
     vardata = Varray2D<double>(nvars);
-    varnmiss = std::vector<std::vector<size_t>>(nvars);
+    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
 
     for (int varID = 0; varID < nvars; ++varID)
       {
         const auto gridsize = varList1[varID].gridsize;
         const auto nlevels = varList1[varID].nlevels;
         vardata[varID].resize(gridsize * nlevels);
-        varnmiss[varID].resize(nlevels);
+        varnumMissVals[varID].resize(nlevels);
       }
 
     for (int i = 1; i < nmul; ++i)
@@ -116,9 +127,9 @@ public:
             const auto offset = gridsize * levelID;
             auto single = &vardata[varID][offset];
 
-            size_t nmiss;
-            cdo_read_record(streamID1, single, &nmiss);
-            varnmiss[varID][levelID] = nmiss;
+            size_t numMissVals;
+            cdo_read_record(streamID1, single, &numMissVals);
+            varnumMissVals[varID][levelID] = numMissVals;
           }
 
         for (int i = 0; i < nmul; ++i)
@@ -131,11 +142,11 @@ public:
               const auto gridsize = varList1[varID].gridsize;
               const auto offset = gridsize * levelID;
               auto single = &vardata[varID][offset];
-              const auto nmiss = varnmiss[varID][levelID];
+              const auto numMissVals = varnumMissVals[varID][levelID];
 
               array_copy(gridsize, single, array.data());
               cdo_def_record(streamID2, varID2, levelID);
-              cdo_write_record(streamID2, array.data(), nmiss);
+              cdo_write_record(streamID2, array.data(), numMissVals);
             }
 
         tsID++;
@@ -147,19 +158,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Pardup(void *process)
-{
-  ModulePardup pardup;
-
-  pardup.init(process);
-  pardup.run();
-  pardup.close();
-
-  return nullptr;
-}
diff --git a/src/Pinfo.cc b/src/Pinfo.cc
index ab4be4988520a9ac0e334eb49be95ce29e071936..5904d90218ed2b890e96f8e182c074d5f8b2bae8 100644
--- a/src/Pinfo.cc
+++ b/src/Pinfo.cc
@@ -16,8 +16,20 @@
 #include "printinfo.h"
 #include "cdo_zaxis.h"
 
-class ModulePinfo
+class Pinfo : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Pinfo",
+    .operators = { { "pinfo"}, { "pinfov"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Pinfo> registration = RegisterEntry<Pinfo>(module);
+  int PINFO, PINFOV;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -32,16 +44,13 @@ class ModulePinfo
 
   VarList varList1;
 
-  int PINFO, PINFOV;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    PINFO = cdo_operator_add("pinfo", 0, 0, nullptr);
-    PINFOV = cdo_operator_add("pinfov", 0, 0, nullptr);
+    PINFO = module.get_id("pinfo");
+    PINFOV = module.get_id("pinfov");
 
     (void) (PINFO);  // unused
 
@@ -98,8 +107,8 @@ public:
 
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             const auto &var = varList1[varID];
             indg += 1;
@@ -114,15 +123,15 @@ public:
             auto level = cdo_zaxis_inq_level(var.zaxisID, levelID);
             fprintf(stdout, " %7g ", level);
 
-            fprintf(stdout, "%7zu %7zu :", gridsize, nmiss);
+            fprintf(stdout, "%7zu %7zu :", gridsize, numMissVals);
 
-            if (gridInqType(var.gridID) == GRID_SPECTRAL || (gridsize == 1 && nmiss == 0))
+            if (gridInqType(var.gridID) == GRID_SPECTRAL || (gridsize == 1 && numMissVals == 0))
               {
                 fprintf(stdout, "            %#12.5g\n", array1[0]);
               }
             else
               {
-                if (nmiss)
+                if (numMissVals)
                   {
                     auto mmm = varray_min_max_mean_mv(gridsize, array1, var.missval);
                     arrmin = mmm.min;
@@ -143,13 +152,13 @@ public:
                 if (gridsize) { fprintf(stdout, "%#12.5g%#12.5g%#12.5g\n", arrmin, arrmean, arrmax); }
                 else { fprintf(stdout, "                     nan\n"); }
 
-                if (imiss != nmiss && nmiss) fprintf(stdout, "Found %zu of %zu missing values!\n", imiss, nmiss);
+                if (imiss != numMissVals && numMissVals) fprintf(stdout, "Found %zu of %zu missing values!\n", imiss, numMissVals);
               }
 
             varray_copy(gridsize, array1, array2);
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
@@ -160,17 +169,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Pinfo(void *process)
-{
-  ModulePinfo pinfo;
-  pinfo.init(process);
-  pinfo.run();
-  pinfo.close();
-  return nullptr;
-}
diff --git a/src/Pressure.cc b/src/Pressure.cc
index d6e660c0dc5a70ad8190ea3c19bd30ed19631e71..cd0e10ab0dc073fcddbb0c2c6eade560a9ec1dd7 100644
--- a/src/Pressure.cc
+++ b/src/Pressure.cc
@@ -8,9 +8,9 @@
 /*
    This module contains the following operators:
 
-      Pressure    pressure_fl          Pressure on full hybrid levels
-      Pressure    pressure_hl          Pressure on half hybrid levels
-      Pressure    deltap               Difference of two half hybrid levels
+      Pressure    pressure             Pressure on model full-levels
+      Pressure    pressure_half        Pressure on model half-levels
+      Pressure    delta_pressure       Difference of two pressure half-levels
 */
 
 #include <cdi.h>
@@ -22,8 +22,25 @@
 #include "stdnametable.h"
 #include "const.h"
 
-class ModulePressure
+class Pressure : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Pressure",
+    // clang-format off
+    .operators = { { "pressure", PressureHelp },
+                   { "pressure_half", PressureHelp },
+                   { "delta_pressure", PressureHelp } },
+    // clang-format on
+    .aliases = { { "pressure_full", "pressure" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Pressure> registration = RegisterEntry<Pressure>(module);
+
+  int PRESSURE_FULL, PRESSURE_HALF, DELTA_PRESSURE;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -42,23 +59,19 @@ class ModulePressure
   Varray<double> psProg;
   Varray<double> vct;
   Varray<double> array;
-  Varray<double> deltap;
-  Varray<double> fullPress;
-  Varray<double> halfPress;
+  Varray<double> deltaPressure;
+  Varray<double> pressureFull;
+  Varray<double> pressureHalf;
 
   int numHybridLevels = 0, numFullLevels = 0, numHalfLevels = 0;
 
-  int PRESSURE_FL, PRESSURE_HL, DELTAP;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    PRESSURE_FL = cdo_operator_add("pressure_fl", 0, 0, nullptr);
-    PRESSURE_HL = cdo_operator_add("pressure_hl", 0, 0, nullptr);
-    DELTAP = cdo_operator_add("deltap", 0, 0, nullptr);
+    PRESSURE_FULL = module.get_id("pressure");
+    PRESSURE_HALF = module.get_id("pressure_half");
+    DELTA_PRESSURE = module.get_id("delta_pressure");
 
     operatorID = cdo_operator_id();
 
@@ -80,13 +93,13 @@ public:
     if (!hasVars3D) cdo_abort("No 3D variable with hybrid sigma pressure coordinate found!");
 
     psProg = Varray<double>(gridsize);
-    deltap = Varray<double>(gridsize * numFullLevels);
-    fullPress = Varray<double>(gridsize * numFullLevels);
-    halfPress = Varray<double>(gridsize * numHalfLevels);
+    deltaPressure = Varray<double>(gridsize * numFullLevels);
+    pressureFull = Varray<double>(gridsize * numFullLevels);
+    pressureHalf = Varray<double>(gridsize * numHalfLevels);
 
     int zaxisID_PL = -1;
     int zaxisType = -1;
-    if (operatorID == PRESSURE_FL || operatorID == DELTAP)
+    if (operatorID == PRESSURE_FULL || operatorID == DELTA_PRESSURE)
       {
         if (numFullLevels == numHybridLevels)
           zaxisID_PL = zaxisID_ML;
@@ -125,8 +138,8 @@ public:
       {
         cdo_print("Found:");
         // clang-format off
-      if (-1 != varIDs.psID)      cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1[varIDs.psID].name);
-      if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), varList1[varIDs.lnpsID].name);
+        if (-1 != varIDs.psID)   cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1[varIDs.psID].name);
+        if (-1 != varIDs.lnpsID) cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), varList1[varIDs.lnpsID].name);
         // clang-format on
       }
 
@@ -170,6 +183,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -189,9 +203,9 @@ public:
 
             if (varID == pvarID)
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array.data(), &nmiss);
-                if (nmiss) cdo_abort("Missing valus unsupported!");
+                size_t numMissVals;
+                cdo_read_record(streamID1, array.data(), &numMissVals);
+                if (numMissVals) cdo_abort("Missing valus unsupported!");
               }
           }
 
@@ -206,29 +220,29 @@ public:
             auto mm = varray_min_max(psProg);
             if (mm.min < MIN_PS || mm.max > MAX_PS) cdo_warning("Surface pressure out of range (min=%g max=%g)!", mm.min, mm.max);
 
-            vct_to_hybrid_pressure(fullPress.data(), halfPress.data(), vct.data(), psProg.data(), numFullLevels, gridsize);
+            vct_to_hybrid_pressure(pressureFull.data(), pressureHalf.data(), vct.data(), psProg.data(), numFullLevels, gridsize);
           }
 
         double *pout = nullptr;
         int nlevels = 0;
-        if (operatorID == PRESSURE_FL)
+        if (operatorID == PRESSURE_FULL)
           {
             nlevels = numFullLevels;
-            pout = fullPress.data();
+            pout = pressureFull.data();
           }
-        else if (operatorID == DELTAP)
+        else if (operatorID == DELTA_PRESSURE)
           {
             nlevels = numFullLevels;
             for (int k = 0; k < numFullLevels; ++k)
               for (size_t i = 0; i < gridsize; ++i)
-                deltap[k * gridsize + i] = halfPress[(k + 1) * gridsize + i] - halfPress[k * gridsize + i];
+                deltaPressure[k * gridsize + i] = pressureHalf[(k + 1) * gridsize + i] - pressureHalf[k * gridsize + i];
 
-            pout = deltap.data();
+            pout = deltaPressure.data();
           }
-        else if (operatorID == PRESSURE_HL)
+        else if (operatorID == PRESSURE_HALF)
           {
             nlevels = numHalfLevels;
-            pout = halfPress.data();
+            pout = pressureHalf.data();
           }
 
         int varID = 0;
@@ -248,17 +262,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Pressure(void *process)
-{
-  ModulePressure pressure;
-  pressure.init(process);
-  pressure.run();
-  pressure.close();
-  return nullptr;
-}
diff --git a/src/Query.cc b/src/Query.cc
index 2230aac10f18d1bcda8641b89001c35ae5411d51..d69af6d45a8859f5549f5e4455e4b0a22979cdb9 100644
--- a/src/Query.cc
+++ b/src/Query.cc
@@ -14,9 +14,21 @@
 #include "pmlist.h"
 #include "cdo_default_values.h"
 
-class ModuleQuery
+class Query : public Process
 {
-  int streamID1; //QueryStream
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Query",
+    .operators = { { "query", 0, 0, "queryentries"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, FilesOnly },
+  };
+  inline static RegisterEntry<Query> registration = RegisterEntry<Query>(module);
+
+  int streamID1;  // QueryStream
   CdoStreamID streamID2;
 
   int taxisID1;
@@ -27,13 +39,9 @@ class ModuleQuery
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("query", 0, 0, "query entries");
-
-    //auto dataIsUnchanged = data_is_unchanged();
+    // auto dataIsUnchanged = data_is_unchanged();
 
     auto operatorID = cdo_operator_id();
 
@@ -47,7 +55,7 @@ public:
     PMList pmlist;
     KVList kvlist;
     kvlist.name = cdo_module_name();
-    if (kvlist.parse_arguments(natts, cdo_get_oper_argv()) != 0) cdo_abort("Parse error!");
+    if (kvlist.parse_arguments(cdo_get_oper_argv()) != 0) cdo_abort("Parse error!");
     if (Options::cdoVerbose) kvlist.print();
 
     auto pkvlist = &kvlist;
@@ -71,7 +79,7 @@ public:
     set_query_parameter(*pkvlist, query);
     if (Options::cdoVerbose) cdiQueryPrint(query);
 
-     streamID1 = streamOpenReadQuery(cdo_get_stream_name(0), query);
+    streamID1 = streamOpenReadQuery(cdo_get_stream_name(0), query);
     if (streamID1 < 0) cdi_open_error(streamID1, "Open failed on >%s<", cdo_get_stream_name(0));
 
     cdiQueryPrintEntriesNotFound(query);
@@ -83,11 +91,11 @@ public:
     auto vlistID1 = streamInqVlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-     taxisID1 = vlistInqTaxis(vlistID1);
-     taxisID2 = taxisDuplicate(taxisID1);
+    taxisID1 = vlistInqTaxis(vlistID1);
+    taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-     streamID2 = cdo_open_write(1);
+    streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
     varList_init(varList1, vlistID1);
@@ -120,9 +128,9 @@ public:
             {
               field.init(varList1[varID]);
               if (field.memType == MemType::Float)
-                streamReadRecordF(streamID1, field.vec_f.data(), &field.nmiss);
+                streamReadRecordF(streamID1, field.vec_f.data(), &field.numMissVals);
               else
-                streamReadRecord(streamID1, field.vec_d.data(), &field.nmiss);
+                streamReadRecord(streamID1, field.vec_d.data(), &field.numMissVals);
               cdo_write_record(streamID2, field);
             }
           }
@@ -136,18 +144,5 @@ public:
   {
     streamClose(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Query(void *process)
-{
-  ModuleQuery query;
-  query.init(process);
-  query.run();
-  query.close();
-
-  return nullptr;
-}
diff --git a/src/Recttocomplex.cc b/src/Recttocomplex.cc
index cd032345bdea3d6daaed07c77348bef8276d88fa..5773837799e005a865d124f6cd78193949b53581 100644
--- a/src/Recttocomplex.cc
+++ b/src/Recttocomplex.cc
@@ -9,8 +9,19 @@
 
 #include "process_int.h"
 
-class ModuleRecttocomplex
+class Recttocomplex : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Recttocomplex",
+    .operators = { { "recttocomplex"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Recttocomplex> registration = RegisterEntry<Recttocomplex>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -28,9 +39,8 @@ class ModuleRecttocomplex
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -54,7 +64,7 @@ public:
         vlistDefVarDatatype(vlistID3, varID, datatype);
       }
 
-     streamID3 = cdo_open_write(2);
+    streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
@@ -87,9 +97,9 @@ public:
 
             cdo_inq_record(streamID2, &varID, &levelID);
 
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
-            cdo_read_record(streamID2, array2.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
+            cdo_read_record(streamID2, array2.data(), &numMissVals);
 
             auto gridsize = varList1[varID].gridsize;
             for (size_t i = 0; i < gridsize; ++i)
@@ -98,7 +108,7 @@ public:
                 array3[2 * i + 1] = array2[i];
               }
 
-            cdo_write_record(streamID3, array3.data(), nmiss);
+            cdo_write_record(streamID3, array3.data(), numMissVals);
           }
 
         tsID++;
@@ -112,19 +122,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID3);
-
-    cdo_finish();
   }
 };
-
-void *
-Recttocomplex(void *process)
-{
-  ModuleRecttocomplex recttocomplex;
-
-  recttocomplex.init(process);
-  recttocomplex.run();
-  recttocomplex.close();
-
-  return nullptr;
-}
diff --git a/src/Regres.cc b/src/Regres.cc
index 87b61d8ba8122ac7c1548a04d9b0148087191f12..0cabd833811c7d029bb8d30c49c0129ea2a9f02a 100644
--- a/src/Regres.cc
+++ b/src/Regres.cc
@@ -34,7 +34,7 @@ regresGetParameter(bool &tstepIsEqual)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -52,8 +52,20 @@ regresGetParameter(bool &tstepIsEqual)
     }
 }
 
-class ModuleRegres
+class Regres : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Regres",
+    .operators = { { "regres", RegresHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Regres> registration = RegisterEntry<Regres>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -74,10 +86,8 @@ class ModuleRegres
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     tstepIsEqual = true;
     regresGetParameter(tstepIsEqual);
 
@@ -137,8 +147,8 @@ public:
 
             recList[recID].set(varID, levelID);
 
-            size_t nmiss;
-            cdo_read_record(streamID1, field1.vec_d.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, field1.vec_d.data(), &numMissVals);
 
             auto gridsize = varList[varID].gridsize;
             auto missval = varList[varID].missval;
@@ -206,18 +216,5 @@ public:
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Regres(void *process)
-{
-  ModuleRegres regress;
-  regress.init(process);
-  regress.run();
-  regress.close();
-
-  return nullptr;
-}
diff --git a/src/Remapeta.cc b/src/Remapeta.cc
index 20b4f847fff1230ce89b3c8cede997b109468a2f..0e9993616e9b2a6b9e0be99a60eed031601149d7 100644
--- a/src/Remapeta.cc
+++ b/src/Remapeta.cc
@@ -11,11 +11,12 @@
       Remapeta     remapeta          Model to model level interpolation
 */
 
+#include <fstream>
+
 #include <cdi.h>
 
 #include "process_int.h"
 #include "cdo_vlist.h"
-#include "readline.h"
 #include "hetaeta.h"
 #include "vertical_interp.h"
 #include "stdnametable.h"
@@ -85,27 +86,23 @@ ncctop(double cptop, long nlev, long nlevp1, const double *vct_a, const double *
 }
 
 static Varray<double>
-vctFromFile(const char *filename)
+vct_from_file(const std::string &filename)
 {
-  char line[1024], *pline;
-  int i = 0;
   constexpr int maxvct = 8192;
-
-  auto fp = std::fopen(filename, "r");
-  if (fp == nullptr)
-    {
-      perror(filename);
-      exit(EXIT_FAILURE);
-    }
-
   Varray<double> vct;
   vct.resize(maxvct);
 
-  while (cdo::readline(fp, line, 1024))
+  std::ifstream file(filename);
+  if (!file.is_open()) cdo_abort("Open failed on: %s\n", filename);
+
+  int i = 0;
+  std::string line;
+  while (std::getline(file, line))
     {
       if (line[0] == '#' || line[0] == '\0') continue;
 
-      pline = line;
+      char *lineCpy = strdup(line.c_str());
+      char *pline = lineCpy;
       auto num = (int) strtod(pline, &pline);
       if (pline == nullptr) cdo_abort("Format error in VCT file %s!", filename);
       if (num != i) cdo_warning("Inconsistent VCT file, entry %d is %d.", i, num);
@@ -117,10 +114,12 @@ vctFromFile(const char *filename)
 
       vct[i + maxvct / 2] = strtod(pline, &pline);
 
+      free(lineCpy);
+
       i++;
     }
 
-  std::fclose(fp);
+  file.close();
 
   auto nvct = 2 * i;
   auto nlevh = i - 1;
@@ -163,15 +162,35 @@ field_copy_array(size_t len, const Field &field, T *array)
 }
 
 #define MAX_VARS3D 1024
-
 template <typename T>
-class ModuleRemapeta
+void
+resize(Varray<T> &arr, size_t new_size)
+{
+  arr.resize(new_size);
+}
+
+class Remapeta : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Remapeta",
+    .operators = { { "remapeta", 0, 0, "VCT filename", RemapetaHelp },
+                   { "remapeta_s", 0, 0, "VCT filename", RemapetaHelp },
+                   { "remapeta_z", 0, 0, "VCT filename", RemapetaHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Remapeta>(module);
+
+  int REMAPETA, REMAPETA_S, REMAPETA_Z;
   double cconst = 1.0E-6;
   size_t nfis2gp = 0;
   int nvars3D = 0;
   Varray<double> fis2;
-  size_t nmissout = 0;
+  size_t numMissValsout = 0;
   bool lfis2 = false;
   int varids[MAX_VARS3D];
   Varray<int> imiss;
@@ -190,8 +209,13 @@ class ModuleRemapeta
   VarIDs varIDs;
 
   Varray<double> ps2;
-  Varray<T> t1, t2;
-  Varray<T> q1, q2;
+
+  Varray<float> f_t1, f_t2;
+  Varray<float> f_q1, f_q2;
+
+  Varray<double> d_t1, d_t2;
+  Varray<double> d_q1, d_q2;
+
   Varray<double> tscor, pscor, secor;
   Varray<double> fis1;
   Varray<double> ps1;
@@ -215,7 +239,8 @@ class ModuleRemapeta
   Varray<double> sum1, sum2;
   Varray<double> deltap1, deltap2;
   Varray<double> halfPress1, halfPress2;
-  Varray2D<T> vars1, vars2;
+  Varray2D<float> f_vars1, f_vars2;
+  Varray2D<double> d_vars1, d_vars2;
 
   Varray<double> vct1;
   Varray<double> vct2;
@@ -226,20 +251,115 @@ class ModuleRemapeta
   double *a2;
   double *b2;
 
-  int REMAPETA_S, REMAPETA_Z;
+  template <typename T>
+  void
+  do_work(long nctop, Varray<T> &t2, Varray<T> &q2, Varray2D<T> &vars1, Varray2D<T> &vars2)
+  {
+    if (ltq)
+      {
+        int varID = varIDs.tempID;
+        for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+          {
+            auto offset = gridsize * levelID;
+            auto single2 = &t2[offset];
+
+            auto mm = array_min_max_mask(gridsize, single2, imiss);
+            if (mm.min < MIN_T || mm.max > MAX_T)
+              cdo_warning("Output temperature at level %d out of range (min=%g max=%g)!", levelID + 1, mm.min, mm.max);
+
+            setmissval(gridsize, imiss, missval, single2);
+            cdo_def_record(streamID2, varID, levelID);
+
+            if (memType == MemType::Float)
+              cdo_write_record_f(streamID2, (float *) single2, numMissValsout);
+            else
+              cdo_write_record(streamID2, (double *) single2, numMissValsout);
+          }
+
+        varID = varIDs.humID;
+        for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+          {
+            auto offset = gridsize * levelID;
+            auto single2 = &q2[offset];
+
+            corr_hum(gridsize, single2, MIN_Q);
+
+            if (levelID < nctop)
+              for (size_t i = 0; i < gridsize; ++i) single2[i] = cconst;
+
+            auto mm = array_min_max_mask(gridsize, single2, imiss);
+            if (mm.min < MIN_Q || mm.max > MAX_Q)
+              cdo_warning("Output humidity at level %d out of range (min=%g max=%g)!", levelID + 1, mm.min, mm.max);
+
+            setmissval(gridsize, imiss, missval, single2);
+            cdo_def_record(streamID2, varID, levelID);
+
+            if (memType == MemType::Float)
+              cdo_write_record_f(streamID2, (float *) single2, numMissValsout);
+            else
+              cdo_write_record(streamID2, (double *) single2, numMissValsout);
+          }
+      }
+
+    for (int iv = 0; iv < nvars3D; ++iv)
+      {
+        int varID = varids[iv];
+
+        auto nlevels = varList2[varID].nlevels;
+
+        if (operatorID == REMAPETA_S)
+          {
+            vertSum(sum1, vars1[iv], gridsize, numFullLevels1);
+            vertSum(sum2, vars2[iv], gridsize, numFullLevels2);
+          }
+        else if (operatorID == REMAPETA_Z)
+          {
+            vct_to_hybrid_pressure((double *) nullptr, halfPress1.data(), vct1.data(), ps1.data(), numFullLevels1, gridsize);
+            for (int k = 0; k < numFullLevels1; ++k)
+              for (size_t i = 0; i < gridsize; ++i)
+                {
+                  deltap1[k * gridsize + i] = halfPress1[(k + 1) * gridsize + i] - halfPress1[k * gridsize + i];
+                  deltap1[k * gridsize + i] = std::log(deltap1[k * gridsize + i]);
+                }
+            vertSumw(sum1, vars1[iv], gridsize, numFullLevels1, deltap1);
+
+            vct_to_hybrid_pressure((double *) nullptr, halfPress2.data(), vct2.data(), ps1.data(), numFullLevels2, gridsize);
+            for (int k = 0; k < numFullLevels2; ++k)
+              for (size_t i = 0; i < gridsize; ++i)
+                {
+                  deltap2[k * gridsize + i] = halfPress2[(k + 1) * gridsize + i] - halfPress2[k * gridsize + i];
+                  deltap2[k * gridsize + i] = std::log(deltap2[k * gridsize + i]);
+                }
+            vertSumw(sum2, vars2[iv], gridsize, numFullLevels2, deltap2);
+          }
+
+        for (int levelID = 0; levelID < nlevels; ++levelID)
+          {
+            auto offset = gridsize * levelID;
+            auto single2 = &vars2[iv][offset];
+
+            if (operatorID == REMAPETA_S || operatorID == REMAPETA_Z)
+              for (size_t i = 0; i < gridsize; ++i) single2[i] = single2[i] * sum1[i] / sum2[i];
+
+            setmissval(gridsize, imiss, missval, single2);
+            cdo_def_record(streamID2, varID, levelID);
+
+            if (memType == MemType::Float)
+              cdo_write_record_f(streamID2, (float *) single2, numMissValsout);
+            else
+              cdo_write_record(streamID2, (double *) single2, numMissValsout);
+          }
+      }
+  }
 
 public:
   void
-  init(void *process)
+  init()
   {
     memType = Options::CDO_Memtype;
 
-    cdo_initialize(process);
-    // clang-format off
-                   cdo_operator_add("remapeta",   0, 0, "VCT file name");
-   REMAPETA_S = cdo_operator_add("remapeta_s", 0, 0, "VCT file name");
-   REMAPETA_Z = cdo_operator_add("remapeta_z", 0, 0, "VCT file name");
-    // clang-format on
+    REMAPETA_S = module.get_id("remapeta_s");
+    REMAPETA_Z = module.get_id("remapeta_z");
 
     operatorID = cdo_operator_id();
 
@@ -256,8 +376,8 @@ public:
           }
       }
 
-    vct2 = vctFromFile(cdo_operator_argv(0).c_str());
-    const int nvct2 = vct2.size();
+    vct2 = vct_from_file(cdo_operator_argv(0));
+    int nvct2 = vct2.size();
     numFullLevels2 = nvct2 / 2 - 1;
 
     a2 = vct2.data();
@@ -282,16 +402,16 @@ public:
 
         fis2.resize(nfis2gp);
 
-        size_t nmiss;
-        streamReadRecord(streamID, fis2.data(), &nmiss);
+        size_t numMissVals;
+        streamReadRecord(streamID, fis2.data(), &numMissVals);
 
-        if (nmiss)
+        if (numMissVals)
           {
             missval = vlistInqVarMissval(vlistID1, varID);
             imiss.resize(nfis2gp);
             for (size_t i = 0; i < nfis2gp; ++i) imiss[i] = dbl_is_equal(fis2[i], missval);
 
-            nmissout = nmiss;
+            numMissValsout = numMissVals;
           }
 
         // check range of surface_geopotential
@@ -431,30 +551,42 @@ public:
     if (lfis2 && gridsize != nfis2gp) cdo_abort("Orographies have different grid size!");
 
     ps2 = Varray<double>(gridsize);
+
     if (ltq)
       {
         tscor.resize(gridsize);
         pscor.resize(gridsize);
         secor.resize(gridsize);
 
-        t1.resize(gridsize * numFullLevels1);
-        q1.resize(gridsize * numFullLevels1);
+        auto gs1 = gridsize * numFullLevels1;
+        auto gs2 = gridsize * numFullLevels2;
+        (memType == MemType::Float) ? resize(f_t1, gs1) : resize(d_t1, gs1);
+        (memType == MemType::Float) ? resize(f_q1, gs1) : resize(d_q1, gs1);
 
-        t2.resize(gridsize * numFullLevels2);
-        q2.resize(gridsize * numFullLevels2);
+        (memType == MemType::Float) ? resize(f_t2, gs2) : resize(d_t2, gs2);
+        (memType == MemType::Float) ? resize(f_q2, gs2) : resize(d_q2, gs2);
       }
 
     if (nvars3D)
       {
-        vars1.resize(nvars);
-        vars2.resize(nvars);
-        for (int varID = 0; varID < nvars3D; ++varID) vars1[varID].resize(gridsize * numFullLevels1);
-        for (int varID = 0; varID < nvars3D; ++varID) vars2[varID].resize(gridsize * numFullLevels2);
+        (memType == MemType::Float) ? resize(f_vars1, nvars) : resize(d_vars1, nvars);
+        (memType == MemType::Float) ? resize(f_vars2, nvars) : resize(d_vars2, nvars);
+
+        if (memType == MemType::Float)
+          {
+            for (int varID = 0; varID < nvars3D; ++varID) f_vars1[varID].resize(gridsize * numFullLevels1);
+            for (int varID = 0; varID < nvars3D; ++varID) f_vars2[varID].resize(gridsize * numFullLevels2);
+          }
+        else
+          {
+            for (int varID = 0; varID < nvars3D; ++varID) d_vars1[varID].resize(gridsize * numFullLevels1);
+            for (int varID = 0; varID < nvars3D; ++varID) d_vars2[varID].resize(gridsize * numFullLevels2);
+          }
       }
 
     if (zaxisID_ML != -1 && varIDs.sgeopotID == -1)
       {
-        varray_fill(fis1, 0.0);
+        ranges::fill(fis1, 0.0);
         if (ltq) cdo_warning("%s not found - set to zero!", var_stdname(surface_geopotential));
       }
 
@@ -481,7 +613,6 @@ public:
   void
   run()
   {
-
     int tsID = 0;
     while (true)
       {
@@ -518,9 +649,11 @@ public:
                       field_copy_array(gridsize, field, ps1.data());
                   }
                 else if (ltq && varID == varIDs.tempID)
-                  field_copy_array(gridsize, field, &t1[offset]);
+                  if (memType == MemType::Float) { field_copy_array(gridsize, field, &f_t1[offset]); }
+                  else { field_copy_array(gridsize, field, &d_t1[offset]); }
                 else if (ltq && varID == varIDs.humID)
-                  field_copy_array(gridsize, field, &q1[offset]);
+                  if (memType == MemType::Float) { field_copy_array(gridsize, field, &f_q1[offset]); }
+                  else { field_copy_array(gridsize, field, &d_q1[offset]); }
                 // else if ( zaxisID == zaxisID_ML )
                 else if (zaxisInqType(zaxisID) == ZAXIS_HYBRID && nlevels == numFullLevels1)
                   {
@@ -530,7 +663,8 @@ public:
 
                     if (i == nvars3D) cdo_abort("Internal error, 3D variable not found!");
 
-                    field_copy_array(gridsize, field, &vars1[i][offset]);
+                    if (memType == MemType::Float) { field_copy_array(gridsize, field, &f_vars1[i][offset]); }
+                    else { field_copy_array(gridsize, field, &d_vars1[i][offset]); }
                   }
                 else
                   {
@@ -565,7 +699,11 @@ public:
             for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
               {
                 auto offset = gridsize * levelID;
-                auto mm = array_min_max_mask(gridsize, &t1[offset], imiss);
+                auto mm = [&]() {
+                  if (memType == MemType::Float) { return array_min_max_mask(gridsize, &f_t1[offset], imiss); }
+                  else { return array_min_max_mask(gridsize, &d_t1[offset], imiss); }
+                }();
+
                 if (mm.min < MIN_T || mm.max > MAX_T)
                   cdo_warning("Input temperature at level %d out of range (min=%g max=%g)!", levelID + 1, mm.min, mm.max);
               }
@@ -574,9 +712,16 @@ public:
             for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
               {
                 auto offset = gridsize * levelID;
-                corr_hum(gridsize, &q1[offset], MIN_Q);
 
-                auto mm = array_min_max_mask(gridsize, &q1[offset], imiss);
+                if (memType == MemType::Float)
+                  corr_hum(gridsize, &f_q1[offset], MIN_Q);
+                else
+                  corr_hum(gridsize, &d_q1[offset], MIN_Q);
+
+                auto mm = [&]() {
+                  if (memType == MemType::Float) { return array_min_max_mask(gridsize, &f_q1[offset], imiss); }
+                  else { return array_min_max_mask(gridsize, &d_q1[offset], imiss); }
+                }();
                 if (mm.min < MIN_Q || mm.max > MAX_Q)
                   cdo_warning("Input humidity at level %d out of range (min=%g max=%g)!", levelID + 1, mm.min, mm.max);
               }
@@ -584,8 +729,12 @@ public:
 
         if (nvars3D || ltq)
           {
-            hetaeta(ltq, gridsize, imiss.data(), numFullLevels1, a1, b1, fis1, ps1, t1, q1, numFullLevels2, a2, b2, fis2, ps2, t2,
-                    q2, nvars3D, vars1, vars2, tscor, pscor, secor);
+            if (memType == MemType::Float)
+              hetaeta(ltq, gridsize, imiss.data(), numFullLevels1, a1, b1, fis1, ps1, f_t1, f_q1, numFullLevels2, a2, b2, fis2, ps2,
+                      f_t2, f_q2, nvars3D, f_vars1, f_vars2, tscor, pscor, secor);
+            else
+              hetaeta(ltq, gridsize, imiss.data(), numFullLevels1, a1, b1, fis1, ps1, d_t1, d_q1, numFullLevels2, a2, b2, fis2, ps2,
+                      d_t2, d_q2, nvars3D, d_vars1, d_vars2, tscor, pscor, secor);
           }
 
         long nctop = (cptop > 0) ? ncctop(cptop, (long) numFullLevels2, (long) numFullLevels2 + 1, a2, b2) : 0;
@@ -596,7 +745,7 @@ public:
             int levelID = 0;
             setmissval(gridsize, imiss, missval, fis2.data());
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, fis2.data(), nmissout);
+            cdo_write_record(streamID2, fis2.data(), numMissValsout);
           }
 
         if (zaxisID_ML != -1 && varIDs.lnpsID != -1)
@@ -608,104 +757,11 @@ public:
             int levelID = 0;
             setmissval(gridsize, imiss, missval, ps2.data());
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, ps2.data(), nmissout);
-          }
-
-        if (ltq)
-          {
-            int varID = varIDs.tempID;
-            for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
-              {
-                auto offset = gridsize * levelID;
-                auto single2 = &t2[offset];
-
-                auto mm = array_min_max_mask(gridsize, single2, imiss);
-                if (mm.min < MIN_T || mm.max > MAX_T)
-                  cdo_warning("Output temperature at level %d out of range (min=%g max=%g)!", levelID + 1, mm.min, mm.max);
-
-                setmissval(gridsize, imiss, missval, single2);
-                cdo_def_record(streamID2, varID, levelID);
-
-                if (memType == MemType::Float)
-                  cdo_write_record_f(streamID2, (float *) single2, nmissout);
-                else
-                  cdo_write_record(streamID2, (double *) single2, nmissout);
-              }
-
-            varID = varIDs.humID;
-            for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
-              {
-                auto offset = gridsize * levelID;
-                auto single2 = &q2[offset];
-
-                corr_hum(gridsize, single2, MIN_Q);
-
-                if (levelID < nctop)
-                  for (size_t i = 0; i < gridsize; ++i) single2[i] = cconst;
-
-                auto mm = array_min_max_mask(gridsize, single2, imiss);
-                if (mm.min < MIN_Q || mm.max > MAX_Q)
-                  cdo_warning("Output humidity at level %d out of range (min=%g max=%g)!", levelID + 1, mm.min, mm.max);
-
-                setmissval(gridsize, imiss, missval, single2);
-                cdo_def_record(streamID2, varID, levelID);
-
-                if (memType == MemType::Float)
-                  cdo_write_record_f(streamID2, (float *) single2, nmissout);
-                else
-                  cdo_write_record(streamID2, (double *) single2, nmissout);
-              }
+            cdo_write_record(streamID2, ps2.data(), numMissValsout);
           }
 
-        for (int iv = 0; iv < nvars3D; ++iv)
-          {
-            int varID = varids[iv];
-
-            auto nlevels = varList2[varID].nlevels;
-
-            if (operatorID == REMAPETA_S)
-              {
-                vertSum(sum1, vars1[iv], gridsize, numFullLevels1);
-                vertSum(sum2, vars2[iv], gridsize, numFullLevels2);
-              }
-            else if (operatorID == REMAPETA_Z)
-              {
-                vct_to_hybrid_pressure((double *) nullptr, halfPress1.data(), vct1.data(), ps1.data(), numFullLevels1, gridsize);
-                for (int k = 0; k < numFullLevels1; ++k)
-                  for (size_t i = 0; i < gridsize; ++i)
-                    {
-                      deltap1[k * gridsize + i] = halfPress1[(k + 1) * gridsize + i] - halfPress1[k * gridsize + i];
-                      deltap1[k * gridsize + i] = std::log(deltap1[k * gridsize + i]);
-                    }
-                vertSumw(sum1, vars1[iv], gridsize, numFullLevels1, deltap1);
-
-                vct_to_hybrid_pressure((double *) nullptr, halfPress2.data(), vct2.data(), ps1.data(), numFullLevels2, gridsize);
-                for (int k = 0; k < numFullLevels2; ++k)
-                  for (size_t i = 0; i < gridsize; ++i)
-                    {
-                      deltap2[k * gridsize + i] = halfPress2[(k + 1) * gridsize + i] - halfPress2[k * gridsize + i];
-                      deltap2[k * gridsize + i] = std::log(deltap2[k * gridsize + i]);
-                    }
-                vertSumw(sum2, vars2[iv], gridsize, numFullLevels2, deltap2);
-              }
-
-            for (int levelID = 0; levelID < nlevels; ++levelID)
-              {
-                auto offset = gridsize * levelID;
-                auto single2 = &vars2[iv][offset];
-
-                if (operatorID == REMAPETA_S || operatorID == REMAPETA_Z)
-                  for (size_t i = 0; i < gridsize; ++i) single2[i] = single2[i] * sum1[i] / sum2[i];
-
-                setmissval(gridsize, imiss, missval, single2);
-                cdo_def_record(streamID2, varID, levelID);
-
-                if (memType == MemType::Float)
-                  cdo_write_record_f(streamID2, (float *) single2, nmissout);
-                else
-                  cdo_write_record(streamID2, (double *) single2, nmissout);
-              }
-          }
+        if (memType == MemType::Float) { do_work(nctop, f_t2, f_q2, f_vars1, f_vars2); }
+        else { do_work(nctop, d_t2, d_q2, d_vars1, d_vars2); }
 
         tsID++;
       }
@@ -714,30 +770,7 @@ public:
   void
   close()
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Remapeta(void *process)
-{
-  if (Options::CDO_Memtype == MemType::Float)
-    {
-      ModuleRemapeta<float> remapeta;
-      remapeta.init(process);
-      remapeta.run();
-      remapeta.close();
-    }
-  else
-    {
-      ModuleRemapeta<double> remapeta;
-      remapeta.init(process);
-      remapeta.run();
-      remapeta.close();
-    }
-
-  return nullptr;
-};
diff --git a/src/Remapgrid.cc b/src/Remapgrid.cc
index d2f8e179c92a52f74a2c4e82d1cf9334f5e41b81..9e77955afb495c126cf0158de300953d59275006 100644
--- a/src/Remapgrid.cc
+++ b/src/Remapgrid.cc
@@ -211,7 +211,7 @@ remap_read_weights(const std::string &remapWeightsFile, int gridID1, int gridID2
   if (gridInqType(gridID1) == GRID_GME) gridsize = remap0.srcGrid.size;
 
   for (size_t i = 0; i < gridsize; ++i)
-    if (remap0.srcGrid.mask[i] == false) remap0.nmiss++;
+    if (remap0.srcGrid.mask[i] == false) remap0.numMissVals++;
 
   auto gridsize2 = gridInqSize(gridID2);
   if (gridInqType(gridID2) == GRID_GME)
@@ -232,7 +232,7 @@ remap_read_weights(const std::string &remapWeightsFile, int gridID1, int gridID2
   return remapSwitches;
 }
 
-double getPlanetRadius(int gridID);
+double get_planet_radius_in_meter(int gridID);
 
 static void
 print_node_info(int gridID2, const RemapVars &rv, const RemapGrid &srcGrid)
@@ -251,16 +251,13 @@ print_node_info(int gridID2, const RemapVars &rv, const RemapGrid &srcGrid)
       auto lon1 = RAD2DEG * llpoint.lon;
       auto lat1 = RAD2DEG * llpoint.lat;
       if (lon1 > 180.0) lon1 -= 360.0;
-      auto distance
-          = orthodrome(DEG2RAD * lon1, DEG2RAD * lat1, DEG2RAD * lon2, DEG2RAD * lat2) * getPlanetRadius(srcGrid.gridID) / 1000;
+      auto distance = orthodrome(DEG2RAD * lon1, DEG2RAD * lat1, DEG2RAD * lon2, DEG2RAD * lat2)
+                      * get_planet_radius_in_meter(srcGrid.gridID) / 1000;
 
       cdo_print("Target Point: lon=%g/lat=%g  Source Point: index=%zu lon=%g/lat=%g distance=%.3fkm", lon2, lat2, srcCellIndex + 1,
                 lon1, lat1, distance);
     }
-  else if (rv.numLinks == 0)
-    {
-      cdo_print("Target Point: lon=%g/lat=%g  Source Point: out of grid area", lon2, lat2);
-    }
+  else if (rv.numLinks == 0) { cdo_print("Target Point: lon=%g/lat=%g  Source Point: out of grid area", lon2, lat2); }
 }
 
 void
@@ -271,26 +268,31 @@ obsolete_operator(const std::string &operatorOld, const std::string &operatorNew
   cdo_warning("%s", outStr);
 }
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("remap",        REMAP,        0, nullptr);
-  cdo_operator_add("remapcon",     REMAPCON,     0, nullptr);
-  cdo_operator_add("remapycon2test",REMAPYCON2,  0, nullptr);
-  cdo_operator_add("remapscon",    REMAPSCON,    0, nullptr);
-  cdo_operator_add("remapscon2",   REMAPSCON2,   0, nullptr);
-  cdo_operator_add("remapbil",     REMAPBIL,     0, nullptr);
-  cdo_operator_add("remapbic",     REMAPBIC,     0, nullptr);
-  cdo_operator_add("remapdis",     REMAPDIS,     0, nullptr);
-  cdo_operator_add("remapnn",      REMAPNN,      0, nullptr);
-  cdo_operator_add("remaplaf",     REMAPLAF,     0, nullptr);
-  cdo_operator_add("remapavgtest", REMAPAVG,     0, nullptr);
-  // clang-format on
-}
-
-class ModuleRemapgrid
+class Remapgrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Remapgrid",
+    // clang-format off
+    .operators = { { "remap", REMAP, 0, RemapHelp },
+                   { "remapbil", REMAPBIL, 0, RemapbilHelp },
+                   { "remapbic", REMAPBIC, 0, RemapbicHelp },
+                   { "remapnn", REMAPNN, 0, RemapnnHelp },
+                   { "remapdis", REMAPDIS, 0, RemapdisHelp },
+                   { "remapcon", REMAPCON, 0, RemapconHelp },
+                   { "remapycon2test", REMAPYCON2, 0, nullptr},
+                   { "remapscon", REMAPSCON, 0, nullptr},
+                   { "remapscon2", REMAPSCON2, 0, nullptr},
+                   { "remaplaf", REMAPLAF, 0, RemaplafHelp },
+                   { "remapavgtest", REMAPAVG, 0, nullptr} },
+    // clang-format on
+    .aliases = { { "remapycon", "remapcon" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Remapgrid> registration = RegisterEntry<Remapgrid>(module);
   RemapSwitches remapSwitches;
   bool remap_genweights = Options::REMAP_genweights;
   int numRemaps = 0;
@@ -330,12 +332,8 @@ class ModuleRemapgrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -368,10 +366,7 @@ public:
             if (inum < 1) cdo_abort("Number of nearest neighbors out of range (>0)!");
             numNeighbors = inum;
           }
-        else
-          {
-            operator_check_argc(1);
-          }
+        else { operator_check_argc(1); }
       }
 
     gridID2 = cdo_define_grid(cdo_operator_argv(0));
@@ -392,7 +387,7 @@ public:
 
     remapGrids = remap_set_grids(vlistID1, varList1);
 
-    auto numRemapGrids = std::count_if(remapGrids.begin(), remapGrids.end(), [](auto flag) { return (flag == true); });
+    auto numRemapGrids = ranges::count_if(remapGrids, [](auto flag) { return (flag == true); });
     if (numRemapGrids == 0) cdo_abort("No remappable grid found!");
 
     auto numGrids = vlistNgrids(vlistID1);
@@ -420,10 +415,7 @@ public:
             // printf("read remapWeightsFile %d: %s\n", numRemaps+1, remapWeightsFile.c_str());
             auto &remap = remaps[numRemaps];
             remapSwitches = remap_read_weights(remapWeightsFile, gridID1, gridID2, remap, extrapolateIsSet, remapExtrapolate);
-            if (numRemaps == 0)
-              {
-                operfunc = maptype_to_operfunc(remapSwitches);
-              }
+            if (numRemaps == 0) { operfunc = maptype_to_operfunc(remapSwitches); }
             else if (operfunc != maptype_to_operfunc(remapSwitches))
               {
                 cdo_abort("Remapping method changed in input weights files!");
@@ -497,7 +489,7 @@ public:
             auto &var = varList1[varID];
             field1.init(var);
             cdo_read_record(streamID1, field1);
-            auto nmiss1 = useMask ? field1.nmiss : 0;
+            auto numMissVals1 = useMask ? field1.numMissVals : 0;
 
             field2.init(varList2[varID]);
 
@@ -511,7 +503,7 @@ public:
 
                 if (gridIsCircular(var.gridID) && !extrapolateIsSet) remapExtrapolate = true;
 
-                remap_set_mask(var.gridsize, field1, nmiss1, var.missval, imask);
+                remap_set_mask(var.gridsize, field1, numMissVals1, var.missval, imask);
 
                 int remapIndex = -1;
                 for (remapIndex = numRemaps - 1; remapIndex >= 0; remapIndex--)
@@ -519,7 +511,7 @@ public:
                     auto &remap = remaps[remapIndex];
                     if (var.gridID == remap.gridID)
                       {
-                        if ((useMask && nmiss1 == remap.nmiss && imask == remap.srcGrid.mask) || !useMask)
+                        if ((useMask && numMissVals1 == remap.numMissVals && imask == remap.srcGrid.mask) || !useMask)
                           {
                             remap.nused++;
                             break;
@@ -577,7 +569,7 @@ public:
                       }
 
                     remap.gridID = var.gridID;
-                    remap.nmiss = nmiss1;
+                    remap.numMissVals = numMissVals1;
 
                     if (var.gridType == GRID_GME)
                       {
@@ -589,16 +581,16 @@ public:
 
                     if (mapType == RemapMethod::CONSERV_SCRIP || mapType == RemapMethod::CONSERV)
                       {
-                        varray_fill(remap.srcGrid.cell_area, 0.0);
-                        varray_fill(remap.srcGrid.cell_frac, 0.0);
-                        varray_fill(remap.tgtGrid.cell_area, 0.0);
+                        ranges::fill(remap.srcGrid.cell_area, 0.0);
+                        ranges::fill(remap.srcGrid.cell_frac, 0.0);
+                        ranges::fill(remap.tgtGrid.cell_area, 0.0);
                       }
-                    varray_fill(remap.tgtGrid.cell_frac, 0.0);
+                    ranges::fill(remap.tgtGrid.cell_frac, 0.0);
 
                     // initialize some remapping variables
                     remap_vars_init(mapType, remapOrder, remap.vars);
 
-                    remap_print_info(operfunc, remap_genweights, remap.srcGrid, remap.tgtGrid, nmiss1, remapSwitches.numNeighbors);
+                    remap_print_info(operfunc, remap_genweights, remap.srcGrid, remap.tgtGrid, numMissVals1, remapSwitches.numNeighbors);
 
                     if (remap_genweights)
                       {
@@ -646,10 +638,7 @@ public:
                     else
                       remap_field(field2, var.missval, gridsize2, remap.vars, field1, gradients);
                   }
-                else
-                  {
-                    remap_field(remapSwitches, remap, field1, field2);
-                  }
+                else { remap_field(remapSwitches, remap, field1, field2); }
 
                 if (operfunc == REMAPSCON || operfunc == REMAPSCON2 || operfunc == REMAPCON || operfunc == REMAPYCON2)
                   {
@@ -666,7 +655,7 @@ public:
 
                 if (gridInqType(gridID2) == GRID_GME) restore_gme_grid(field2, remap.tgtGrid);
 
-                field2.nmiss = field_num_mv(field2);
+                field2.numMissVals = field_num_mv(field2);
               }
 
             cdo_def_record(streamID2, varID, levelID);
@@ -677,7 +666,7 @@ public:
       }
 
     if (doRemap && remap_genweights && remaps[0].nused == 0)
-      remap_print_warning(remapWeightsFiles[0], operfunc, remaps[0].srcGrid, remaps[0].nmiss);
+      remap_print_warning(remapWeightsFiles[0], operfunc, remaps[0].srcGrid, remaps[0].numMissVals);
 
     for (int remapIndex = 0; remapIndex < numRemaps; remapIndex++)
       {
@@ -696,18 +685,5 @@ public:
     cdo_stream_close(streamID1);
 
     gridDestroy(gridID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Remapgrid(void *process)
-{
-  ModuleRemapgrid remapGrid;
-  remapGrid.init(process);
-  remapGrid.run();
-  remapGrid.close();
-
-  return nullptr;
-}
diff --git a/src/Remapstat.cc b/src/Remapstat.cc
index 1f5a78810afb605b71a530ffb44d930c68acc29e..a2cfb4df5ea08d4b94b39a4062c33fe2fbb2e112 100644
--- a/src/Remapstat.cc
+++ b/src/Remapstat.cc
@@ -424,7 +424,7 @@ get_bin_num(double lon1, double lat1, size_t numBins, const std::vector<double>
 }
 
 static void
-check_vmask(std::vector<char> vmask)
+check_vmask(const std::vector<char> &vmask)
 {
   constexpr int maxVals = 128;
   size_t vm[maxVals] = { 0 };
@@ -540,10 +540,7 @@ gen_mapdata(int gridID1, int gridID2)
                                       : calc_maxdist(i, nv, lon2, lat2, xbounds, ybounds);
 
           if (maxdist < 2.0) { maxdist = 1.01 * std::sqrt(maxdist); }
-          else
-            {
-              cdo_abort("Search radius of target grid cell[%zu] > 90 degrees!", i + 1);
-            }
+          else { cdo_abort("Search radius of target grid cell[%zu] > 90 degrees!", i + 1); }
 
           auto numIndices = grid_point_search_distance_qnearest(gps, maxdist, lon2, lat2, ndistMax, indices, dist);
           // printf("%zu numIndices %zu\n", i+1, numIndices);
@@ -582,30 +579,30 @@ gen_mapdata(int gridID1, int gridID2)
 
 template <typename T>
 static T
-remap_kernel(int operfunc, const Varray<size_t> &indices, size_t &nmiss2, Field &field, Varray<T> &fieldvec, const Varray<T> &vec1,
+remap_kernel(int operfunc, const Varray<size_t> &indices, size_t &numMissVals2, Field &field, Varray<T> &fieldvec, const Varray<T> &vec1,
              T missval)
 {
   T value;
   auto nvalues = indices.size();
   if (nvalues)
     {
-      field.nmiss = 0;
+      field.numMissVals = 0;
       for (size_t k = 0; k < nvalues; ++k)
         {
           auto v1 = vec1[indices[k]];
           fieldvec[k] = v1;
-          if (dbl_is_equal(v1, missval)) field.nmiss++;
+          if (dbl_is_equal(v1, missval)) field.numMissVals++;
         }
 
       field.size = nvalues;
       field.missval = missval;
       value = field_function(field, operfunc);
-      if (dbl_is_equal(value, missval)) nmiss2++;
+      if (dbl_is_equal(value, missval)) numMissVals2++;
     }
   else
     {
       value = missval;
-      nmiss2++;
+      numMissVals2++;
     }
 
   return value;
@@ -619,9 +616,9 @@ remap_field(const Varray2D<size_t> &mapdata, const Field &field1, Field &field2,
   auto gridsize2 = gridInqSize(field2.grid);
   cdo::timer timer;
 
-  size_t nmiss2 = 0;
+  size_t numMissVals2 = 0;
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static) reduction(+ : nmiss2)
+#pragma omp parallel for default(shared) schedule(static) reduction(+ : numMissVals2)
 #endif
   for (size_t i = 0; i < gridsize2; ++i)
     {
@@ -639,9 +636,9 @@ remap_field(const Varray2D<size_t> &mapdata, const Field &field1, Field &field2,
 
       double rvalue = 0.0;
       if (field1.memType == MemType::Float)
-        rvalue = remap_kernel(operfunc, indices, nmiss2, field, field.vec_f, field1.vec_f, (float) missval);
+        rvalue = remap_kernel(operfunc, indices, numMissVals2, field, field.vec_f, field1.vec_f, (float) missval);
       else
-        rvalue = remap_kernel(operfunc, indices, nmiss2, field, field.vec_d, field1.vec_d, missval);
+        rvalue = remap_kernel(operfunc, indices, numMissVals2, field, field.vec_d, field1.vec_d, missval);
 
       if (field2.memType == MemType::Float)
         field2.vec_f[i] = rvalue;
@@ -649,33 +646,36 @@ remap_field(const Varray2D<size_t> &mapdata, const Field &field1, Field &field2,
         field2.vec_d[i] = rvalue;
     }
 
-  field2.nmiss = nmiss2;
+  field2.numMissVals = numMissVals2;
 
   if (Options::cdoVerbose) cdo_print("Remap: %.3f seconds", timer.elapsed());
 }
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("remaprange",  FieldFunc_Range,  0, nullptr);
-  cdo_operator_add("remapmin",    FieldFunc_Min,    0, nullptr);
-  cdo_operator_add("remapmax",    FieldFunc_Max,    0, nullptr);
-  cdo_operator_add("remapsum",    FieldFunc_Sum,    0, nullptr);
-  cdo_operator_add("remapmean",   FieldFunc_Mean,   0, nullptr);
-  cdo_operator_add("remapavg",    FieldFunc_Avg,    0, nullptr);
-  cdo_operator_add("remapstd",    FieldFunc_Std,    0, nullptr);
-  cdo_operator_add("remapstd1",   FieldFunc_Std1,   0, nullptr);
-  cdo_operator_add("remapvar",    FieldFunc_Var,    0, nullptr);
-  cdo_operator_add("remapvar1",   FieldFunc_Var1,   0, nullptr);
-  cdo_operator_add("remapskew",   FieldFunc_Skew,   0, nullptr);
-  cdo_operator_add("remapkurt",   FieldFunc_Kurt,   0, nullptr);
-  cdo_operator_add("remapmedian", FieldFunc_Median, 0, nullptr);
-  // clang-format on
-}
-
-class ModuleRemapstat
+class Remapstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Remapstat",
+    .operators = { { "remaprange", FieldFunc_Range, 0, RemapstatHelp },
+                   { "remapmin", FieldFunc_Min, 0, RemapstatHelp },
+                   { "remapmax", FieldFunc_Max, 0, RemapstatHelp },
+                   { "remapsum", FieldFunc_Sum, 0, RemapstatHelp },
+                   { "remapmean", FieldFunc_Mean, 0, RemapstatHelp },
+                   { "remapavg", FieldFunc_Avg, 0, RemapstatHelp },
+                   { "remapstd", FieldFunc_Std, 0, RemapstatHelp },
+                   { "remapstd1", FieldFunc_Std1, 0, RemapstatHelp },
+                   { "remapvar", FieldFunc_Var, 0, RemapstatHelp },
+                   { "remapvar1", FieldFunc_Var1, 0, RemapstatHelp },
+                   { "remapskew", FieldFunc_Skew, 0, RemapstatHelp },
+                   { "remapkurt", FieldFunc_Kurt, 0, RemapstatHelp },
+                   { "remapmedian", FieldFunc_Median, 0, RemapstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Remapstat> registration = RegisterEntry<Remapstat>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -690,11 +690,8 @@ class ModuleRemapstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -787,17 +784,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Remapstat(void *process)
-{
-  ModuleRemapstat remapstat;
-  remapstat.init(process);
-  remapstat.run();
-  remapstat.close();
-  return nullptr;
-}
diff --git a/src/Remapweights.cc b/src/Remapweights.cc
index a29fe5dfafb7ac43113d6380e31b6ed8089cf680..9c73da285d9e7f67f1be0f4596a8e217533ec087 100644
--- a/src/Remapweights.cc
+++ b/src/Remapweights.cc
@@ -44,7 +44,7 @@ get_parameter(int offset, int &neighbors, bool &map3D, std::string &grid)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -65,7 +65,7 @@ get_parameter(int offset, int &neighbors, bool &map3D, std::string &grid)
 }
 
 static void
-remap_write_weights(const std::string remapWeightsFile, const RemapSwitches &remapSwitches, RemapType &remap)
+remap_write_weights(const std::string &remapWeightsFile, const RemapSwitches &remapSwitches, RemapType &remap)
 {
   remap_write_data_scrip(remapWeightsFile, remapSwitches, remap.srcGrid, remap.tgtGrid, remap.vars);
 
@@ -76,24 +76,28 @@ remap_write_weights(const std::string remapWeightsFile, const RemapSwitches &rem
   remap_search_free(remap.search);
 }
 
-static void
-add_operators(void)
+class Remapweights : public Process
 {
-  // clang-format off
-  cdo_operator_add("gencon",       GENCON,       0, nullptr);
-  cdo_operator_add("genycon2test", GENYCON2,     0, nullptr);
-  cdo_operator_add("genscon",      GENSCON,      0, nullptr);
-  cdo_operator_add("genscon2",     GENSCON2,     0, nullptr);
-  cdo_operator_add("genbil",       GENBIL,       0, nullptr);
-  cdo_operator_add("genbic",       GENBIC,       0, nullptr);
-  cdo_operator_add("gendis",       GENDIS,       0, nullptr);
-  cdo_operator_add("gennn",        GENNN,        0, nullptr);
-  cdo_operator_add("genlaf",       GENLAF,       0, nullptr);
-  // clang-format on
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Remapweights",
+    .operators = { { "genbil", GENBIL, 0, RemapbilHelp },
+                   { "genbic", GENBIC, 0, RemapbicHelp },
+                   { "gennn", GENNN, 0, RemapnnHelp },
+                   { "gendis", GENDIS, 0, RemapdisHelp },
+                   { "gencon", GENCON, 0, RemapconHelp },
+                   { "genycon2test", GENYCON2, 0, nullptr},
+                   { "genscon", GENSCON, 0, nullptr},
+                   { "genscon2", GENSCON2, 0, nullptr},
+                   { "genlaf", GENLAF, 0, RemaplafHelp } },
+    .aliases = { { "genycon", "gencon" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Remapweights> registration = RegisterEntry<Remapweights>(module);
 
-class ModuleRemapweights
-{
   RemapSwitches remapSwitches;
   int numRemaps{ 0 };
   int numNeighbors{ 0 };
@@ -128,11 +132,8 @@ class ModuleRemapweights
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -161,10 +162,7 @@ public:
             if (numNeighborsParam > 0) numNeighbors = numNeighborsParam;
           }
       }
-    else
-      {
-        operator_check_argc(1);
-      }
+    else { operator_check_argc(1); }
 
     gridID2 = cdo_define_grid(targetGrid);
     if (gridInqType(gridID2) == GRID_GENERIC) cdo_abort("Unsupported target grid type (generic)!");
@@ -177,7 +175,7 @@ public:
 
     remapGrids = remap_set_grids(vlistID1, varList1);
 
-    auto numRemapGrids = std::count_if(remapGrids.begin(), remapGrids.end(), [](auto flag) { return (flag == true); });
+    auto numRemapGrids = ranges::count_if(remapGrids, [](auto flag) { return (flag == true); });
     if (numRemapGrids == 0) cdo_abort("No remappable grid found!");
 
     maxRemaps = remapParams.maxRemaps;
@@ -225,24 +223,21 @@ public:
         auto &var = varList1[varID];
         field1.init(var);
         cdo_read_record(streamID1, field1);
-        auto nmiss1 = useMask ? field1.nmiss : 0;
+        auto numMissVals1 = useMask ? field1.numMissVals : 0;
 
         auto gridIndex = vlistGridIndex(vlistID1, var.gridID);
 
         if (remapGrids[gridIndex])
           {
             if (numRemaps == 0) { gridIDout = var.gridID; }
-            else if (gridIDout != var.gridID)
-              {
-                continue;
-              }
+            else if (gridIDout != var.gridID) { continue; }
 
             if (mapType != RemapMethod::CONSERV_SCRIP && mapType != RemapMethod::CONSERV && var.gridType == GRID_GME)
               cdo_abort("Only conservative remapping is available to remap between GME grids!");
 
             if (gridIsCircular(var.gridID) && !extrapolateIsSet) remapExtrapolate = true;
 
-            remap_set_mask(var.gridsize, field1, nmiss1, var.missval, imask);
+            remap_set_mask(var.gridsize, field1, numMissVals1, var.missval, imask);
 
             int remapIndex = -1;
             for (remapIndex = numRemaps - 1; remapIndex >= 0; remapIndex--)
@@ -250,7 +245,7 @@ public:
                 auto &remap = remaps[remapIndex];
                 if (var.gridID == remap.gridID)
                   {
-                    if ((useMask && nmiss1 == remap.nmiss && imask == remap.srcGrid.mask) || !useMask)
+                    if ((useMask && numMissVals1 == remap.numMissVals && imask == remap.srcGrid.mask) || !useMask)
                       {
                         remap.nused++;
                         break;
@@ -290,7 +285,7 @@ public:
             remap_search_init(mapType, remap.search, remap.srcGrid, remap.tgtGrid);
 
             remap.gridID = var.gridID;
-            remap.nmiss = nmiss1;
+            remap.numMissVals = numMissVals1;
 
             if (var.gridType == GRID_GME)
               {
@@ -302,16 +297,16 @@ public:
 
             if (mapType == RemapMethod::CONSERV_SCRIP || mapType == RemapMethod::CONSERV)
               {
-                varray_fill(remap.srcGrid.cell_area, 0.0);
-                varray_fill(remap.srcGrid.cell_frac, 0.0);
-                varray_fill(remap.tgtGrid.cell_area, 0.0);
+                ranges::fill(remap.srcGrid.cell_area, 0.0);
+                ranges::fill(remap.srcGrid.cell_frac, 0.0);
+                ranges::fill(remap.tgtGrid.cell_area, 0.0);
               }
-            varray_fill(remap.tgtGrid.cell_frac, 0.0);
+            ranges::fill(remap.tgtGrid.cell_frac, 0.0);
 
             // initialize some remapping variables
             remap_vars_init(mapType, remapOrder, remap.vars);
 
-            remap_print_info(operfunc, remap_genweights, remap.srcGrid, remap.tgtGrid, nmiss1, remapSwitches.numNeighbors);
+            remap_print_info(operfunc, remap_genweights, remap.srcGrid, remap.tgtGrid, numMissVals1, remapSwitches.numNeighbors);
 
             if (needGradients && remap.srcGrid.rank != 2 && remapOrder == 2)
               {
@@ -350,18 +345,5 @@ public:
   close()
   {
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Remapweights(void *process)
-{
-  ModuleRemapweights remapweights;
-  remapweights.init(process);
-  remapweights.run();
-  remapweights.close();
-
-  return nullptr;
-}
diff --git a/src/Replace.cc b/src/Replace.cc
index 363c4d7bebba54c90b1a1616c9dd4ed2036a2cca..abf0032548f7944f0886c51b35d181dac5cb3fbd 100644
--- a/src/Replace.cc
+++ b/src/Replace.cc
@@ -18,12 +18,23 @@
 #include "cdo_vlist.h"
 #include "cdo_zaxis.h"
 
-class ModuleReplace
+class Replace : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Replace",
+    .operators = { { "replace", ReplaceHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Replace> registration = RegisterEntry<Replace>(module);
   static const int MaxVars = 1024;
   int nchvars = 0;
   int idx;
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -36,7 +47,7 @@ class ModuleReplace
 
   int vars1[MaxVars], vars2[MaxVars];
   std::vector<std::vector<int>> varlevel;
-  std::vector<std::vector<size_t>> varnmiss2;
+  std::vector<std::vector<size_t>> varnumMissVals2;
 
   Varray<double> array;
   VarList varList1, varList2;
@@ -44,9 +55,8 @@ class ModuleReplace
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -101,7 +111,7 @@ public:
     if (nchvars)
       {
         vardata2.resize(nchvars);
-        varnmiss2.resize(nchvars);
+        varnumMissVals2.resize(nchvars);
         varlevel.resize(nchvars);
         for (idx = 0; idx < nchvars; idx++)
           {
@@ -111,7 +121,7 @@ public:
             auto nlevel2 = varList2[varID2].nlevels;
             auto gridsize = varList2[varID2].gridsize;
             vardata2[idx].resize(nlevel2 * gridsize);
-            varnmiss2[idx].resize(nlevel2);
+            varnumMissVals2[idx].resize(nlevel2);
             varlevel[idx].resize(nlevel1);
             /*
             for ( levelID = 0; levelID < nlevel1; levelID++ )
@@ -178,8 +188,8 @@ public:
                   if (vars2[idx] == varID)
                     {
                       auto offset = varList2[varID].gridsize * levelID;
-                      cdo_read_record(streamID2, &vardata2[idx][offset], &nmiss);
-                      varnmiss2[idx][levelID] = nmiss;
+                      cdo_read_record(streamID2, &vardata2[idx][offset], &numMissVals);
+                      varnumMissVals2[idx][levelID] = numMissVals;
                       break;
                     }
               }
@@ -202,15 +212,15 @@ public:
                     {
                       auto offset = varList1[varID].gridsize * levelID2;
                       parray = &vardata2[idx][offset];
-                      nmiss = varnmiss2[idx][levelID2];
+                      numMissVals = varnumMissVals2[idx][levelID2];
                       break;
                     }
                 }
 
-            if (idx == nchvars) cdo_read_record(streamID1, parray, &nmiss);
+            if (idx == nchvars) cdo_read_record(streamID1, parray, &numMissVals);
 
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, parray, nmiss);
+            cdo_write_record(streamID3, parray, numMissVals);
           }
 
         tsID++;
@@ -222,17 +232,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Replace(void *process)
-{
-  ModuleReplace replace;
-  replace.init(process);
-  replace.run();
-  replace.close();
-  return nullptr;
-}
diff --git a/src/Replacevalues.cc b/src/Replacevalues.cc
index c32a8d7fe88e47631bc2e7a47ceb4f238d61ef47..b87bceea987c78980fed473dafb40bf80eab34cd 100644
--- a/src/Replacevalues.cc
+++ b/src/Replacevalues.cc
@@ -18,8 +18,22 @@
 #include "process_int.h"
 #include "param_conversion.h"
 
-class ModuleReplacevalues
+class Replacevalues : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Replacevalues",
+    .operators = { { "setvals", 0, 0, "I1,O1,...,In,On", ReplacevaluesHelp },
+                   { "setrtoc", 0, 0, "range(min,max),value", ReplacevaluesHelp },
+                   { "setrtoc2", 0, 0, "range(min,max),value1,value2", ReplacevaluesHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Replacevalues> registration = RegisterEntry<Replacevalues>(module);
+  int SETVALS, SETRTOC, SETRTOC2;
   int nvals = 0;
   std::vector<double> fltarr;
   double rmin = 0, rmax = 0;
@@ -36,17 +50,14 @@ class ModuleReplacevalues
   Varray<double> array;
   VarList varList1;
 
-  int SETVALS, SETRTOC, SETRTOC2;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-    SETVALS = cdo_operator_add("setvals", 0, 0, "I1,O1,...,In,On");
-    SETRTOC = cdo_operator_add("setrtoc", 0, 0, "range (min, max), value");
-    SETRTOC2 = cdo_operator_add("setrtoc2", 0, 0, "range (min, max), value1, value2");
+    SETVALS = module.get_id("setvals");
+    SETRTOC = module.get_id("setrtoc");
+    SETRTOC2 = module.get_id("setrtoc2");
 
     operatorID = cdo_operator_id();
 
@@ -110,8 +121,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             const auto gridsize = varList1[varID].gridsize;
             const auto missval = varList1[varID].missval;
@@ -146,7 +157,7 @@ public:
               }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_write_record(streamID2, array.data(), numMissVals);
           }
 
         tsID++;
@@ -157,18 +168,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Replacevalues(void *process)
-{
-  ModuleReplacevalues replaveValues;
-  replaveValues.init(process);
-  replaveValues.run();
-  replaveValues.close();
-
-  return nullptr;
-}
diff --git a/src/Rhopot.cc b/src/Rhopot.cc
index 8d8cbf0ec1f4fa3f6a948da0dd0af5455c98f013..9431edadfa6b4d91538d2906108a5d18ec119221 100644
--- a/src/Rhopot.cc
+++ b/src/Rhopot.cc
@@ -141,8 +141,20 @@ calc_rhopot(size_t gridsize, size_t nlevel, const Varray<double> &pressure, cons
     }
 }
 
-class ModuleRhopot
+class Rhopot : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Rhopot",
+    .operators = { { "rhopot", RhopotHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Rhopot> registration = RegisterEntry<Rhopot>(module);
+
   int zaxisID;
   int toID = -1, saoID = -1, thoID = -1;
   double pin = -1;
@@ -166,10 +178,8 @@ class ModuleRhopot
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     if (cdo_operator_argc() == 1) pin = parameter_to_double(cdo_operator_argv(0));
 
     streamID1 = cdo_open_read(0);
@@ -186,20 +196,20 @@ public:
             auto stdname = string_to_lower(cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME));
 
             // clang-format off
-          if      (varname == "to") code = 20;
-          else if (varname == "sao") code = 5;
-          else if (varname == "tho") code = 2;
-          else if (varname == "s") code = 5;
-          else if (varname == "t") code = 2;
-          else if (stdname == "sea_water_salinity") code = 5;
-          else if (stdname == "sea_water_potential_temperature") code = 2;
+            if      (varname == "to") code = 20;
+            else if (varname == "sao") code = 5;
+            else if (varname == "tho") code = 2;
+            else if (varname == "s") code = 5;
+            else if (varname == "t") code = 2;
+            else if (stdname == "sea_water_salinity") code = 5;
+            else if (stdname == "sea_water_potential_temperature") code = 2;
             // clang-format on
           }
 
         // clang-format off
-      if      (code == 20) toID = varID;
-      else if (code == 5) saoID = varID;
-      else if (code == 2) thoID = varID;
+        if      (code == 20) toID = varID;
+        else if (code == 5) saoID = varID;
+        else if (code == 2) thoID = varID;
         // clang-format on
       }
 
@@ -274,6 +284,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -290,8 +301,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            if (varID == toID) cdo_read_record(streamID1, to[levelID].vec_d.data(), &to[levelID].nmiss);
-            if (varID == saoID) cdo_read_record(streamID1, sao[levelID].vec_d.data(), &sao[levelID].nmiss);
+            if (varID == toID) cdo_read_record(streamID1, to[levelID].vec_d.data(), &to[levelID].numMissVals);
+            if (varID == saoID) cdo_read_record(streamID1, sao[levelID].vec_d.data(), &sao[levelID].numMissVals);
           }
 
         calc_rhopot(gridsize, nlevel, pressure, to, sao, rho);
@@ -313,18 +324,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Rhopot(void *process)
-{
-  ModuleRhopot rhopot;
-
-  rhopot.init(process);
-  rhopot.run();
-  rhopot.close();
-
-  return nullptr;
-}
diff --git a/src/Rotuv.cc b/src/Rotuv.cc
index 440132771e8b77525781b4badfec8ae13dfe4a90..6ebcb1468e4fece4190828a22a45355e20c5d309 100644
--- a/src/Rotuv.cc
+++ b/src/Rotuv.cc
@@ -54,8 +54,19 @@ rot_uv_back(int gridID, Varray<double> &us, Varray<double> &vs)
 
 #define MAXARG 16384
 
-class ModuleRotuv
+class Rotuv : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Rotuv",
+    .operators = { { "rotuvb", RotuvbHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Rotuv> registration = RegisterEntry<Rotuv>(module);
   int chcodes[MAXARG];
   const char *chvars[MAXARG];
 
@@ -73,13 +84,12 @@ class ModuleRotuv
   VarList varList1;
   Varray3D<double> vardata;
   std::vector<RecordInfo> recList;
-  std::vector<std::vector<size_t>> varnmiss;
+  std::vector<std::vector<size_t>> varnumMissVals;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("pairs of u and v in the rotated system");
 
@@ -118,7 +128,7 @@ public:
 
     recList = std::vector<RecordInfo>(maxrecs);
 
-    varnmiss = std::vector<std::vector<size_t>>(nvars);
+    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
     vardata = Varray3D<double>(nvars);
 
     bool lfound[MAXARG];
@@ -154,7 +164,7 @@ public:
 
         auto gridsize = gridInqSize(gridID);
         auto nlevels = varList1[varID].nlevels;
-        varnmiss[varID].resize(nlevels);
+        varnumMissVals[varID].resize(nlevels);
         vardata[varID].resize(nlevels);
         for (int levelID = 0; levelID < nlevels; ++levelID) vardata[varID][levelID].resize(gridsize);
       }
@@ -186,8 +196,8 @@ public:
 
             recList[recID].set(varID, levelID);
 
-            cdo_read_record(streamID1, vardata[varID][levelID].data(), &varnmiss[varID][levelID]);
-            if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+            cdo_read_record(streamID1, vardata[varID][levelID].data(), &varnumMissVals[varID][levelID]);
+            if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
           }
 
         for (int i = 0; i < nch; i += 2)
@@ -248,7 +258,7 @@ public:
           {
             auto [varID, levelID] = recList[recID].get();
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, vardata[varID][levelID].data(), varnmiss[varID][levelID]);
+            cdo_write_record(streamID2, vardata[varID][levelID].data(), varnumMissVals[varID][levelID]);
           }
 
         tsID++;
@@ -260,18 +270,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Rotuv(void *process)
-{
-  ModuleRotuv rotuv;
-  rotuv.init(process);
-  rotuv.run();
-  rotuv.close();
-
-  return nullptr;
-}
diff --git a/src/Runpctl.cc b/src/Runpctl.cc
index 2b20138a98ac54745458a22a089dadd2706431f7..c9e8815047992cf4ce1a965dfa2361a317814f9f 100644
--- a/src/Runpctl.cc
+++ b/src/Runpctl.cc
@@ -26,7 +26,7 @@ static size_t
 runpctl(double pn, int ndates, size_t gridsize, Varray<T> &v2, T missval, const FieldVector3D &vars1, int varID, int levelID,
         MemType memType)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   Varray2D<T> array_2D(Threading::ompNumThreads, Varray<T>(ndates));
 
 #ifdef _OPENMP
@@ -59,25 +59,36 @@ runpctl(double pn, int ndates, size_t gridsize, Varray<T> &v2, T missval, const
       else
         {
           v2[i] = missval;
-          nmiss++;
+          numMissVals++;
         }
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 static void
 runpctl(double pn, int ndates, Field &field1, const FieldVector3D &vars1, int varID, int levelID)
 {
   if (field1.memType == MemType::Float)
-    field1.nmiss
+    field1.numMissVals
         = runpctl(pn, ndates, field1.gridsize, field1.vec_f, (float) field1.missval, vars1, varID, levelID, field1.memType);
   else
-    field1.nmiss = runpctl(pn, ndates, field1.gridsize, field1.vec_d, field1.missval, vars1, varID, levelID, field1.memType);
+    field1.numMissVals = runpctl(pn, ndates, field1.gridsize, field1.vec_d, field1.missval, vars1, varID, levelID, field1.memType);
 }
 
-class ModuleRunpctl
+class Runpctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Runpctl",
+    .operators = { { "runpctl", RunpctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Runpctl> registration = RegisterEntry<Runpctl>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   int vlistID1;
@@ -97,12 +108,10 @@ class ModuleRunpctl
 
 public:
   void
-  init(void *process)
+  init()
   {
     constexpr auto timestatDate{ TimeStat::MEAN };
 
-    cdo_initialize(process);
-
     operator_input_arg("percentile number, number of timesteps");
     operator_check_argc(2);
     pn = parameter_to_double(cdo_operator_argv(0));
@@ -224,18 +233,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Runpctl(void *process)
-{
-  ModuleRunpctl pctl;
-  pctl.init(process);
-  pctl.run();
-  pctl.close();
-
-  return nullptr;
-}
diff --git a/src/Runstat.cc b/src/Runstat.cc
index 6e3919354f04b3071afac004189abb124722185d..88e3126985ca315b15cb3cfb2d8ba3430bde3a9d 100644
--- a/src/Runstat.cc
+++ b/src/Runstat.cc
@@ -27,25 +27,28 @@
 #include "datetime.h"
 #include "field_functions.h"
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("runrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("runmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("runmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("runsum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("runmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("runavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("runvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("runvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("runstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("runstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-
-class ModuleRunstat
+class Runstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Runstat",
+    .operators = { { "runrange", FieldFunc_Range, 0, RunstatHelp },
+                   { "runmin", FieldFunc_Min, 0, RunstatHelp },
+                   { "runmax", FieldFunc_Max, 0, RunstatHelp },
+                   { "runsum", FieldFunc_Sum, 0, RunstatHelp },
+                   { "runmean", FieldFunc_Mean, 0, RunstatHelp },
+                   { "runavg", FieldFunc_Avg, 0, RunstatHelp },
+                   { "runstd", FieldFunc_Std, 0, RunstatHelp },
+                   { "runstd1", FieldFunc_Std1, 0, RunstatHelp },
+                   { "runvar", FieldFunc_Var, 0, RunstatHelp },
+                   { "runvar1", FieldFunc_Var1, 0, RunstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Runstat> registration = RegisterEntry<Runstat>(module);
   TimeStat timestatDate{ TimeStat::MEAN };
   bool runstat_nomiss = false;
 
@@ -74,9 +77,8 @@ class ModuleRunstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     auto envstr = getenv("RUNSTAT_NOMISS");
     if (envstr)
@@ -86,8 +88,6 @@ public:
         if (envval == 1) runstat_nomiss = true;
       }
 
-    add_operators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);  // used in omp loop
 
@@ -189,11 +189,11 @@ public:
             cdo_read_record(streamID1, rvars1);
             if (lrange)
               {
-                vars2[numSteps][varID][levelID].nmiss = rvars1.nmiss;
+                vars2[numSteps][varID][levelID].numMissVals = rvars1.numMissVals;
                 vars2[numSteps][varID][levelID].vec_d = rvars1.vec_d;
               }
 
-            if (runstat_nomiss && rvars1.nmiss) cdo_abort("Missing values supported was swichted off by env. RUNSTAT_NOMISS!");
+            if (runstat_nomiss && rvars1.numMissVals) cdo_abort("Missing values supported was swichted off by env. RUNSTAT_NOMISS!");
 
             if (!runstat_nomiss)
               {
@@ -314,18 +314,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Runstat(void *process)
-{
-  ModuleRunstat runstat;
-  runstat.init(process);
-  runstat.run();
-  runstat.close();
-
-  return nullptr;
-}
diff --git a/src/Samplegrid.cc b/src/Samplegrid.cc
index 4802a70cbabd381bc4fbdc7a674ad0a6c0ac812c..feb78ea1f0b1d5a512df394685f4c40d33e12441 100644
--- a/src/Samplegrid.cc
+++ b/src/Samplegrid.cc
@@ -24,11 +24,11 @@
 static void
 sampleData(const double *array1, int gridID1, double *array2, int gridID2, int resampleFactor)
 {
-  const auto nlon1 = gridInqXsize(gridID1);
-  const auto nlat1 = gridInqYsize(gridID1);
+  auto nlon1 = gridInqXsize(gridID1);
+  auto nlat1 = gridInqYsize(gridID1);
 
-  const auto nlon2 = gridInqXsize(gridID2);
-  const auto nlat2 = gridInqYsize(gridID2);
+  auto nlon2 = gridInqXsize(gridID2);
+  auto nlat2 = gridInqYsize(gridID2);
 
   if (cdoDebugExt >= 100)
     cdo_print("%s(): (nlon1: %zu; nlat1: %zu) => (nlon2: %zu; nlat2: %zu); "
@@ -42,9 +42,9 @@ sampleData(const double *array1, int gridID1, double *array2, int gridID2, int r
 static void
 cropData(double *array1, int gridID1, double *array2, int gridID2, int subI0, int subI1, int subJ0, int subJ1)
 {
-  const long nlon1 = gridInqXsize(gridID1);
-  const long nlon2 = gridInqXsize(gridID2);
-  const long rowLen = subI1 - subI0 + 1;  // must be same as nlon1
+  long nlon1 = gridInqXsize(gridID1);
+  long nlon2 = gridInqXsize(gridID2);
+  long rowLen = subI1 - subI0 + 1;  // must be same as nlon1
 
   if (rowLen != nlon2) cdo_abort("cropData() rowLen!= nlon2 [%d != %d]", rowLen, nlon2);
 
@@ -58,8 +58,22 @@ cropData(double *array1, int gridID1, double *array2, int gridID2, int subI0, in
     }
 }
 
-class ModuleSamplegrid
+class Samplegrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Samplegrid",
+    .operators = { { "samplegrid", 0, 0, "resample factor, typically 2 (which will half the resolution)", SamplegridHelp },
+                   { "subgrid", 0, 0, "sub-grid indices: i0,i1,j0,j1", SamplegridHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Samplegrid> registration = RegisterEntry<Samplegrid>(module);
+
+  int SAMPLEGRID, SUBGRID;
   int resampleFactor = 0;
   int subI0 = 0, subI1 = 0, subJ0 = 0, subJ1 = 0;
   struct sbox_t
@@ -86,16 +100,12 @@ class ModuleSamplegrid
   size_t gridsize2;
   std::vector<sbox_t> sbox;
 
-  int SAMPLEGRID, SUBGRID;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    SAMPLEGRID = cdo_operator_add("samplegrid", 0, 0, "resample factor, typically 2 (which will half the resolution)");
-    SUBGRID = cdo_operator_add("subgrid", 0, 0, " sub-grid indices: i0,i1,j0,j1");
+    SAMPLEGRID = module.get_id("samplegrid");
+    SUBGRID = module.get_id("subgrid");
 
     operatorID = cdo_operator_id();
 
@@ -187,13 +197,14 @@ public:
 
     Debug(cdoDebugExt, "gridsize = %ld, gridsize2 = %ld", gridsize, gridsize2);
   }
+
   void
   run()
   {
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -203,8 +214,8 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             cdo_def_record(streamID2, varID, levelID);
 
@@ -212,7 +223,7 @@ public:
 
             if (vars[varID])
               {
-                const auto gridSrcID = vlistInqVarGrid(vlistID1, varID);
+                auto gridSrcID = vlistInqVarGrid(vlistID1, varID);
 
                 int index;
                 for (index = 0; index < ngrids; ++index)
@@ -220,7 +231,7 @@ public:
 
                 if (index == ngrids) cdo_abort("Internal problem, grid not found!");
 
-                const int gridIDsampled = sbox[index].gridIDsampled;
+                int gridIDsampled = sbox[index].gridIDsampled;
                 gridsize2 = gridInqSize(gridIDsampled);
 
                 if (operatorID == SAMPLEGRID)
@@ -232,20 +243,21 @@ public:
                     cropData(array1.data(), gridSrcID, array2.data(), gridIDsampled, subI0, subI1, subJ0, subJ1);
                   }
 
-                if (nmiss)
+                if (numMissVals)
                   {
-                    const auto missval = vlistInqVarMissval(vlistID2, varID);
-                    nmiss = varray_num_mv(gridsize2, array2, missval);
+                    auto missval = vlistInqVarMissval(vlistID2, varID);
+                    numMissVals = varray_num_mv(gridsize2, array2, missval);
                   }
 
-                cdo_write_record(streamID2, array2.data(), nmiss);
+                cdo_write_record(streamID2, array2.data(), numMissVals);
               }
-            else { cdo_write_record(streamID2, array1.data(), nmiss); }
+            else { cdo_write_record(streamID2, array1.data(), numMissVals); }
           }
 
         tsID++;
       }
   }
+
   void
   close()
   {
@@ -253,17 +265,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Samplegrid(void *process)
-{
-  ModuleSamplegrid samplegrid;
-  samplegrid.init(process);
-  samplegrid.run();
-  samplegrid.close();
-  return nullptr;
-}
diff --git a/src/Samplegridicon.cc b/src/Samplegridicon.cc
index 9e210bb05282f4439a55287fea76abcbabd2f619..90cbaa5e0b026ffc2d2455a09487b73b6806679f 100644
--- a/src/Samplegridicon.cc
+++ b/src/Samplegridicon.cc
@@ -5,8 +5,6 @@
 
 */
 
-#include <algorithm>  // sort
-
 #include <cdi.h>
 
 #include "cdo_options.h"
@@ -95,11 +93,11 @@ read_cellindex(const std::string &filename, CellIndex &cellindex)
   for (int recID = 0; recID < nrecs; ++recID)
     {
       int varID, levelID;
-      size_t nmiss;
+      size_t numMissVals;
       streamInqRecord(streamID, &varID, &levelID);
       if (varID == pid /* || varID == nid || varID == cid */)
         {
-          streamReadRecord(streamID, data.data(), &nmiss);
+          streamReadRecord(streamID, data.data(), &numMissVals);
           // if (varID == pid)
           {
             if (Options::cdoVerbose) cdo_print("Read parent_cell_index");
@@ -184,12 +182,6 @@ struct SortInfo
   int p, i;
 };
 
-static bool
-cmpsinfo(const SortInfo &a, const SortInfo &b)
-{
-  return a.p < b.p;
-}
-
 static void
 compute_child_from_parent(CellIndex &cellindex1, CellIndex &cellindex2)
 {
@@ -210,7 +202,7 @@ compute_child_from_parent(CellIndex &cellindex1, CellIndex &cellindex2)
             sinfo[j].p = parent1[j];
             sinfo[j].i = idx1[j];
           }
-        std::sort(sinfo.begin(), sinfo.end(), cmpsinfo);
+        ranges::sort(sinfo, {}, &SortInfo::p);
         for (long j = 0; j < ncells1; ++j)
           {
             parent1[j] = sinfo[j].p;
@@ -263,7 +255,8 @@ read_coordinates(const std::string &filename, long n, Varray<double> &lon, Varra
 }
 
 static void
-read_coordinates(const std::string &filename, long n, Varray<double> &lon, Varray<double> &lat, int nv, Varray<double> &lon_bnds, Varray<double> &lat_bnds)
+read_coordinates(const std::string &filename, long n, Varray<double> &lon, Varray<double> &lat, int nv, Varray<double> &lon_bnds,
+                 Varray<double> &lat_bnds)
 {
   auto streamID = streamOpenRead(filename.c_str());
   auto vlistID = streamInqVlist(streamID);
@@ -300,8 +293,8 @@ constexpr int MAX_SEARCH = 128;  // the triangles are distorted!
 
 static void
 compute_child_from_bounds(CellIndex &cellindex2, long ncells2, Varray<double> &grid_center_lon2, Varray<double> &grid_center_lat2,
-                          Varray<double> &grid_corner_lon2, Varray<double> &grid_corner_lat2, long ncells1, const Varray<double> &grid_center_lon1,
-                          const Varray<double> &grid_center_lat1)
+                          Varray<double> &grid_corner_lon2, Varray<double> &grid_corner_lat2, long ncells1,
+                          const Varray<double> &grid_center_lon1, const Varray<double> &grid_center_lat1)
 {
   if (Options::cdoVerbose) cdo_print("%s", __func__);
 
@@ -466,8 +459,20 @@ samplegrid(double missval, long nci, Varray<CellIndex> &cellindex, const Varray<
     }
 }
 
-class ModuleSamplegridicon
+class Samplegridicon : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Samplegridicon",
+    .operators = { { "samplegridicon", 0, 0, "samplegrids"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 2, OnlyFirst },
+  };
+  inline static RegisterEntry<Samplegridicon> registration = RegisterEntry<Samplegridicon>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -491,12 +496,8 @@ class ModuleSamplegridicon
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("samplegridicon", 0, 0, "sample grids");
-
     nsamplegrids = cdo_operator_argc();
     if (nsamplegrids < 2) cdo_abort("Parameter missing!");
 
@@ -575,20 +576,20 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto missval = vlistInqVarMissval(vlistID1, varID);
 
             samplegrid(missval, nsamplegrids, cellindex, array1, array2, array3);
 
-            nmiss = varray_num_mv(gridsize2, array2, missval);
+            numMissVals = varray_num_mv(gridsize2, array2, missval);
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
 
-            nmiss = varray_num_mv(gridsize2, array3, missval);
+            numMissVals = varray_num_mv(gridsize2, array3, missval);
             cdo_def_record(streamID3, varID, levelID);
-            cdo_write_record(streamID3, array3.data(), nmiss);
+            cdo_write_record(streamID3, array3.data(), numMissVals);
           }
 
         tsID++;
@@ -604,18 +605,5 @@ public:
 
     vlistDestroy(vlistID2);
     gridDestroy(gridID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Samplegridicon(void *process)
-{
-  ModuleSamplegridicon samplegridicon;
-  samplegridicon.init(process);
-  samplegridicon.run();
-  samplegridicon.close();
-
-  return nullptr;
-}
diff --git a/src/Seascount.cc b/src/Seascount.cc
index fcecf5781da17dd7f365504dbfdb70e83a8f4f58..a3efafe858ebd64a882ce62438bd052fdfee7244 100644
--- a/src/Seascount.cc
+++ b/src/Seascount.cc
@@ -21,8 +21,19 @@
 #include "process_int.h"
 #include "field_functions.h"
 
-class ModuleSeascount
+class Seascount : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Seascount",
+    .operators = { { "seascount"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Seascount> registration = RegisterEntry<Seascount>(module);
   CdiDateTime vDateTime0{};
   int seas0 = 0;
   int oldmon = 0;
@@ -42,11 +53,8 @@ class ModuleSeascount
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("seascount", 0, 0, nullptr);
 
     operator_check_argc(0);
 
@@ -123,7 +131,7 @@ public:
                 if (numSets == 0)
                   {
                     for (size_t i = 0; i < fieldsize; ++i) vars1[varID][levelID].vec_d[i] = vars1[varID][levelID].missval;
-                    vars1[varID][levelID].nmiss = fieldsize;
+                    vars1[varID][levelID].numMissVals = fieldsize;
                   }
 
                 field.init(var);
@@ -148,7 +156,7 @@ public:
             if (otsID && varList1[varID].isConstant) continue;
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, vars1[varID][levelID].vec_d.data(), vars1[varID][levelID].nmiss);
+            cdo_write_record(streamID2, vars1[varID][levelID].vec_d.data(), vars1[varID][levelID].numMissVals);
           }
 
         if (nrecs == 0) break;
@@ -160,17 +168,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Seascount(void *process)
-{
-  ModuleSeascount seascount;
-  seascount.init(process);
-  seascount.run();
-  seascount.close();
-  return nullptr;
-}
diff --git a/src/Seasmonstat.cc b/src/Seasmonstat.cc
index df28bc486530eb87f259f790f66c648eda6e808b..4cac345819833f6a9c452e939704005566eac76c 100644
--- a/src/Seasmonstat.cc
+++ b/src/Seasmonstat.cc
@@ -22,8 +22,19 @@
 #include "cdo_season.h"
 #include "field_functions.h"
 
-class ModuleSeasmonstat
+class Seasmonstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Seasmonstat",
+    .operators = { { "seasmonmean", FieldFunc_Mean, 0, nullptr}, { "seasmonavg", FieldFunc_Avg, 0, nullptr} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Seasmonstat> registration = RegisterEntry<Seasmonstat>(module);
   TimeStat timestatDate{ TimeStat::MEAN };
   CdiDateTime vDateTime0{};
   CdiDateTime vDateTime1{};
@@ -55,13 +66,10 @@ class ModuleSeasmonstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    cdo_operator_add("seasmonmean",  FieldFunc_Mean, 0, nullptr);
-    cdo_operator_add("seasmonavg",   FieldFunc_Avg,  0, nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -175,7 +183,7 @@ public:
 
                     fieldc_mul(rvars1, dpm);
 
-                    if (rvars1.nmiss || !rsamp1.empty())
+                    if (rvars1.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(fieldsize);
                         field2_vinit(rsamp1, rvars1, dpm);
@@ -188,7 +196,7 @@ public:
 
                     fieldc_mul(field, dpm);
 
-                    if (field.nmiss || !rsamp1.empty())
+                    if (field.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(fieldsize, dsets);
                         field2_vincr(rsamp1, field, dpm);
@@ -253,18 +261,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Seasmonstat(void *process)
-{
-  ModuleSeasmonstat seasmonstat;
-  seasmonstat.init(process);
-  seasmonstat.run();
-  seasmonstat.close();
-
-  return nullptr;
-}
diff --git a/src/Seaspctl.cc b/src/Seaspctl.cc
index 769166468b8b84eeac8b790e9efc117822999580..b0230087f7124fbae786ac279865c91a92261106 100644
--- a/src/Seaspctl.cc
+++ b/src/Seaspctl.cc
@@ -24,8 +24,19 @@
 #include "cdo_season.h"
 #include "field_functions.h"
 
-class ModuleSeaspctl
+class Seaspctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Seaspctl",
+    .operators = { { "seaspctl", FieldFunc_Pctl, 0, SeaspctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Seaspctl> registration = RegisterEntry<Seaspctl>(module);
   TimeStat timestatDate{ TimeStat::MEAN };
   int seas0 = 0;
   int oldmon = 0;
@@ -52,11 +63,8 @@ class ModuleSeaspctl
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("seaspctl", FieldFunc_Pctl, 0, nullptr);
 
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
@@ -232,18 +240,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Seaspctl(void *process)
-{
-  ModuleSeaspctl seaspctl;
-  seaspctl.init(process);
-  seaspctl.run();
-  seaspctl.close();
-
-  return nullptr;
-}
diff --git a/src/Seasstat.cc b/src/Seasstat.cc
index 43e7ad27cd6ce167f5cbfc76b661fa25c4203fd2..5a3293cf8a2b619f0a75e30a0d68542271fee5dc 100644
--- a/src/Seasstat.cc
+++ b/src/Seasstat.cc
@@ -29,25 +29,28 @@
 #include "cdo_season.h"
 #include "field_functions.h"
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("seasrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("seasmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("seasmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("seassum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("seasmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("seasavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("seasvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("seasvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("seasstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("seasstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-
-class ModuleSeasstat
+class Seasstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Seasstat",
+    .operators = { { "seasrange", FieldFunc_Range, 0, SeasstatHelp },
+                   { "seasmin", FieldFunc_Min, 0, SeasstatHelp },
+                   { "seasmax", FieldFunc_Max, 0, SeasstatHelp },
+                   { "seassum", FieldFunc_Sum, 0, SeasstatHelp },
+                   { "seasmean", FieldFunc_Mean, 0, SeasstatHelp },
+                   { "seasavg", FieldFunc_Avg, 0, SeasstatHelp },
+                   { "seasstd", FieldFunc_Std, 0, SeasstatHelp },
+                   { "seasstd1", FieldFunc_Std1, 0, SeasstatHelp },
+                   { "seasvar", FieldFunc_Var, 0, SeasstatHelp },
+                   { "seasvar1", FieldFunc_Var1, 0, SeasstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Seasstat> registration = RegisterEntry<Seasstat>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -81,11 +84,8 @@ class ModuleSeasstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -195,11 +195,11 @@ public:
                     cdo_read_record(streamID1, rvars1);
                     if (lrange)
                       {
-                        vars2[varID][levelID].nmiss = rvars1.nmiss;
+                        vars2[varID][levelID].numMissVals = rvars1.numMissVals;
                         vars2[varID][levelID].vec_d = rvars1.vec_d;
                       }
 
-                    if (rvars1.nmiss || !rsamp1.empty())
+                    if (rvars1.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                         field2_vinit(rsamp1, rvars1);
@@ -210,7 +210,7 @@ public:
                     field.init(var);
                     cdo_read_record(streamID1, field);
 
-                    if (field.nmiss || !rsamp1.empty())
+                    if (field.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                         field2_vincr(rsamp1, field);
@@ -297,18 +297,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Seasstat(void *process)
-{
-  ModuleSeasstat seasstat;
-  seasstat.init(process);
-  seasstat.run();
-  seasstat.close();
-
-  return nullptr;
-}
diff --git a/src/Selbox.cc b/src/Selbox.cc
index 5800a68f162c5386166007f63c6d9896378344c6..5a1f8c9cce0e81b1ca009453d899844d9fc837a8 100644
--- a/src/Selbox.cc
+++ b/src/Selbox.cc
@@ -985,7 +985,11 @@ get_selboxInfo(int vlistID1, int vlistID2, bool operIndexBox)
           || (!operIndexBox && (gridtype == GRID_UNSTRUCTURED || is_healpix_grid(gridID1))))
         {
           if (operIndexBox) { selboxInfo.push_back(selbox_index_grid(gridID1)); }
-          else { selboxInfo.push_back((gridtype == GRID_UNSTRUCTURED || is_healpix_grid(gridID1)) ? selbox_cell_grid(gridID1) : selbox_lonlat_grid(gridID1)); }
+          else
+            {
+              selboxInfo.push_back((gridtype == GRID_UNSTRUCTURED || is_healpix_grid(gridID1)) ? selbox_cell_grid(gridID1)
+                                                                                               : selbox_lonlat_grid(gridID1));
+            }
 
           vlistChangeGridIndex(vlistID2, index, selboxInfo.back().gridID2);
         }
@@ -1042,8 +1046,22 @@ get_grid_index(int gridID, const std::vector<SelboxInfo> &selboxInfo)
   return index;
 }
 
-class ModuleSelbox
+class Selbox : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selbox",
+    .operators = { { "sellonlatbox", 0, 0, "western and eastern longitude and southern and northern latitude", SelboxHelp },
+                   { "selindexbox", 0, 0, "index of first and last longitude and index of first and last latitude", SelboxHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Selbox> registration = RegisterEntry<Selbox>(module);
+
+  int SELLONLATBOX, SELINDEXBOX;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -1054,9 +1072,6 @@ class ModuleSelbox
 
   VarList varList1, varList2;
 
-  int SELLONLATBOX;
-  int SELINDEXBOX;
-
   int vlistID2;
   int operatorID;
 
@@ -1065,14 +1080,10 @@ class ModuleSelbox
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    SELLONLATBOX = cdo_operator_add("sellonlatbox", 0, 0, "western and eastern longitude and southern and northern latitude");
-    SELINDEXBOX  = cdo_operator_add("selindexbox",  0, 0, "index of first and last longitude and index of first and last latitude");
-    // clang-format on
+    SELLONLATBOX = module.get_id("sellonlatbox");
+    SELINDEXBOX = module.get_id("selindexbox");
 
     operatorID = cdo_operator_id();
 
@@ -1130,7 +1141,7 @@ public:
                 else
                   window_box(field1, field2, sb.lat1, sb.lat2, sb.lon11, sb.lon12, sb.lon21, sb.lon22);
 
-                if (field1.nmiss) field_num_mv(field2);
+                if (field1.numMissVals) field_num_mv(field2);
 
                 cdo_write_record(streamID2, field2);
               }
@@ -1148,18 +1159,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Selbox(void *process)
-{
-  ModuleSelbox selbox;
-  selbox.init(process);
-  selbox.run();
-  selbox.close();
-
-  return nullptr;
-}
diff --git a/src/Select.cc b/src/Select.cc
index 8a6a4c5d8c604cc23dd1f66cd6dc5cd9c64610fe..2d7d88cc48636267c15db44fc1e7976c316b9ec6 100644
--- a/src/Select.cc
+++ b/src/Select.cc
@@ -52,9 +52,9 @@ write_const_vars(CdoStreamID streamID2, const VarList &varList2, int nvars, Varr
           for (int levelID2 = 0; levelID2 < var.nlevels; ++levelID2)
             {
               auto pdata = &vardata2[varID2][var.gridsize * levelID2];
-              auto nmiss = array_num_mv(var.gridsize, pdata, var.missval);
+              auto numMissVals = array_num_mv(var.gridsize, pdata, var.missval);
               cdo_def_record(streamID2, varID2, levelID2);
-              cdo_write_record(streamID2, pdata, nmiss);
+              cdo_write_record(streamID2, pdata, numMissVals);
             }
           vardata2[varID2].clear();
           vardata2[varID2].shrink_to_fit();
@@ -128,8 +128,21 @@ datetime_to_double(CdiDateTime dateTime)
   return fdatetime;
 }
 
-class ModuleSelect
+class Select : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Select",
+    .operators = { { "select", 0, 0, "parameter list", SelectHelp }, { "delete", 0, 0, "parameter list", SelectHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Select> registration = RegisterEntry<Select>(module);
+
+  int SELECT, DELETE;
   CdoStreamID streamID2 = CDO_STREAM_UNDEF;
   int tsID2 = 0;
   int vlistID0 = -1, vlistID2 = -1;
@@ -138,16 +151,12 @@ class ModuleSelect
   KVList kvlist;
   int operatorID;
 
-  int SELECT, DELETE;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    SELECT = cdo_operator_add("select", 0, 0, "parameter list");
-    DELETE = cdo_operator_add("delete", 0, 0, "parameter list");
+    SELECT = module.get_id("select");
+    DELETE = module.get_id("delete");
 
     dataIsUnchanged = data_is_unchanged();
 
@@ -161,7 +170,7 @@ public:
     if (argc == 0) cdo_abort("Parameter missing!");
 
     kvlist.name = cdo_module_name();
-    if (kvlist.parse_arguments(argc, argnames) != 0) cdo_abort("Parse error!");
+    if (kvlist.parse_arguments(argnames) != 0) cdo_abort("Parse error!");
     if (Options::cdoVerbose) kvlist.print();
 
     auto kv = kvlist.search("timestepmask");
@@ -348,7 +357,7 @@ public:
                           {
                             levidx = levelID + 1;
                             level = cdo_zaxis_inq_level(zaxisID, levelID);
-                            if (!processVars[varID] && SELINFO_CHECK(levidx)) processVars[varID] = true;
+                            if (!processVars[varID] && SELINFO_CHECK_INDEX(levidx, var.nlevels)) processVars[varID] = true;
                             if (!processVars[varID] && SELINFO_CHECK(level)) processVars[varID] = true;
                             if (!processVars[varID] && SELINFO_CHECK_RANGE(levrange, level)) processVars[varID] = true;
                           }
@@ -388,7 +397,7 @@ public:
                           {
                             if (SELINFO_NVAL(levidx))
                               {
-                                if (SELINFO_CHECK(levidx)) vlistDefFlag(vlistID1, varID, levelID, xresult);
+                                if (SELINFO_CHECK_INDEX(levidx, var.nlevels)) vlistDefFlag(vlistID1, varID, levelID, xresult);
                               }
                             else if (SELINFO_NVAL(level))
                               {
@@ -398,10 +407,7 @@ public:
                               {
                                 if (SELINFO_CHECK_RANGE(levrange, level)) vlistDefFlag(vlistID1, varID, levelID, xresult);
                               }
-                            else
-                              {
-                                vlistDefFlag(vlistID1, varID, levelID, xresult);
-                              }
+                            else { vlistDefFlag(vlistID1, varID, levelID, xresult); }
                           }
                       }
                   }
@@ -437,10 +443,7 @@ public:
                         for (int levelID = 0; levelID < var.nlevels; ++levelID) vlistDefFlag(vlistID1, varID, levelID, true);
                       }
                   }
-                else
-                  {
-                    cdo_abort("No variable selected!");
-                  }
+                else { cdo_abort("No variable selected!"); }
               }
 
             // if (Options::cdoVerbose) vlistPrint(vlistID1);
@@ -520,10 +523,7 @@ public:
             if (SELINFO_NVAL(startdate)) fstartdate = date_str_to_double(startdate, 0);
             if (SELINFO_NVAL(enddate)) fenddate = date_str_to_double(enddate, 1);
           }
-        else
-          {
-            vlist_compare(vlistID0, vlistID1, CmpVlist::All);
-          }
+        else { vlist_compare(vlistID0, vlistID1, CmpVlist::All); }
 
         if (nvars2 == 0)
           {
@@ -686,8 +686,8 @@ public:
                           {
                             auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
                             if (levelID == 0) vardata2[varID2].resize(var.gridsize * var.nlevels);
-                            size_t nmiss;
-                            cdo_read_record(streamID1, &vardata2[varID2][var.gridsize * levelID2], &nmiss);
+                            size_t numMissVals;
+                            cdo_read_record(streamID1, &vardata2[varID2][var.gridsize * levelID2], &numMissVals);
                           }
                       }
                   }
@@ -726,18 +726,5 @@ public:
     vlistDestroy(vlistID2);
 
     if (tsID2 == 0) cdo_abort("No timesteps selected!");
-
-    cdo_finish();
   }
 };
-
-void *
-Select(void *process)
-{
-  ModuleSelect select;
-  select.init(process);
-  select.run();
-  select.close();
-
-  return nullptr;
-}
diff --git a/src/Selgridcell.cc b/src/Selgridcell.cc
index 4f3790cb1d1b5e36195c72ac9e06072fc01d0cf9..7480c4ac591bdb616a610bdbb214bfd1b765defb 100644
--- a/src/Selgridcell.cc
+++ b/src/Selgridcell.cc
@@ -31,10 +31,7 @@ gen_grid_unstr_from_healpix(int gridID1, size_t gridsize2, const std::vector<lon
 
   auto [nside, order] = cdo::get_healpix_params(gridID1);
 
-  for (size_t i = 0; i < gridsize2; ++i)
-    {
-      hp_index_to_lonlat(order, nside, cellidx[i], &xvals[i], &yvals[i]);
-    }
+  for (size_t i = 0; i < gridsize2; ++i) { hp_index_to_lonlat(order, nside, cellidx[i], &xvals[i], &yvals[i]); }
 
   auto gridID2 = gridCreate(GRID_UNSTRUCTURED, gridsize2);
   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_UNITS, "radians");
@@ -95,8 +92,22 @@ select_index(const Field &field1, Field &field2, long nind, const std::vector<lo
     select_index(field1.vec_d, field2.vec_d, nind, cellidx);
 }
 
-class ModuleSelgridcell
+class Selgridcell : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selgridcell",
+    .operators = { { "selgridcell", 0, 0, "gridcell indices(1-N)", SelgridcellHelp },
+                   { "delgridcell", 0, 0, "gridcell indices(1-N)", SelgridcellHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Selgridcell> registration = RegisterEntry<Selgridcell>(module);
+
+  int DELGRIDCELL;
   struct sindex_t
   {
     int gridID1, gridID2;
@@ -124,15 +135,12 @@ class ModuleSelgridcell
 
 public:
   void
-  init(void *process)
+  init()
   {
     int nind = 0;
     std::vector<int> indarr;
 
-    cdo_initialize(process);
-
-    cdo_operator_add("selgridcell", 0, 0, "grid cell indices (1-N)");
-    auto DELGRIDCELL = cdo_operator_add("delgridcell", 0, 0, "grid cell indices (1-N)");
+    DELGRIDCELL = module.get_id("delgridcell");
 
     operator_input_arg(cdo_operator_enter(0));
 
@@ -196,10 +204,8 @@ public:
 
     if (nind == 0) cdo_abort("Argument %s generates no input!", cdo_operator_argv(0));
 
-    auto minmax = std::minmax_element(indarr.begin(), indarr.end());
-    auto indmin = *minmax.first;
-    auto indmax = *minmax.second;
-    if (indmin < 0) cdo_abort("Index < 1 not allowed!");
+    const auto [indmin, indmax] = ranges::minmax_element(indarr);
+    if (*indmin < 0) cdo_abort("Index < 1 not allowed!");
 
     streamID1 = cdo_open_read(0);
 
@@ -245,7 +251,7 @@ public:
 
         auto gridsize = gridInqSize(gridID1);
         if (gridsize == 1) continue;
-        if ((size_t) indmax >= gridsize)
+        if ((size_t) *indmax >= gridsize)
           {
             cdo_warning("Max grid index is greater than grid size, skipped grid %d!", index + 1);
             continue;
@@ -307,7 +313,7 @@ public:
                 field2.init(varList2[varID]);
                 select_index(field1, field2, ncells, cellidx);
 
-                if (field1.nmiss) field2.nmiss = field_num_mv(field2);
+                if (field1.numMissVals) field2.numMissVals = field_num_mv(field2);
 
                 cdo_write_record(streamID2, field2);
               }
@@ -325,17 +331,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Selgridcell(void *process)
-{
-  ModuleSelgridcell selgridcell;
-  selgridcell.init(process);
-  selgridcell.run();
-  selgridcell.close();
-
-  return nullptr;
-}
diff --git a/src/Selmulti.cc b/src/Selmulti.cc
index 625df34c8bf23081c6a20a4378e7cafa372581ec..92dee562348987d49fd6ece4ffcb1507bee9042d 100644
--- a/src/Selmulti.cc
+++ b/src/Selmulti.cc
@@ -5,11 +5,12 @@
 
 */
 
+#include <fstream>
+
 #include <cdi.h>
 
 #include "process_int.h"
 #include "cdo_zaxis.h"
-#include "readline.h"
 #include "cdi_lockedIO.h"
 
 // NOTE: All operators in this module works only on GRIB edition 1 files!
@@ -149,8 +150,23 @@ int getNumberOfDeleteSelectionTuples();
 
 int multiSelectionParser(const char *filenameOrString);
 
-class ModuleSelmulti
+class Selmulti : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selmulti",
+    .operators = { { "selmulti", 0, 0, "filename/string with selection specification", SelmultiHelp },
+                   { "delmulti", 0, 0, "filename/string with selection specification", SelmultiHelp },
+                   { "changemulti", 0, 0, "filename/string with selection specification", SelmultiHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Selmulti> registration = RegisterEntry<Selmulti>(module);
+
+  int SELMULTI, DELMULTI, CHANGEMULTI;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -169,20 +185,13 @@ class ModuleSelmulti
   VarList varList1;
   Varray<double> array;
 
-  int SELMULTI, DELMULTI, CHANGEMULTI;
-
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    // clang-format off
-   SELMULTI    = cdo_operator_add("selmulti",    0, 0, "filename/string with selection specification");
-   DELMULTI    = cdo_operator_add("delmulti",    0, 0, "filename/string with selection specification");
-   CHANGEMULTI = cdo_operator_add("changemulti", 0, 0, "filename/string with selection specification");
-    // clang-format on
+    SELMULTI = module.get_id("selmulti");
+    DELMULTI = module.get_id("delmulti");
+    CHANGEMULTI = module.get_id("changemulti");
 
     operatorID = cdo_operator_id();
 
@@ -477,8 +486,8 @@ public:
                   }
                 else
                   {
-                    size_t nmiss;
-                    cdo_read_record(streamID1, array.data(), &nmiss);
+                    size_t numMissVals;
+                    cdo_read_record(streamID1, array.data(), &numMissVals);
 
                     if (!simpleMath)
                       {
@@ -501,7 +510,7 @@ public:
                             }
                       }
 
-                    cdo_write_record(streamID2, array.data(), nmiss);
+                    cdo_write_record(streamID2, array.data(), numMissVals);
                   }  // end of else ( lcopy )
               }      // end if ( vlistInqFlag(vlistID1, varID, levelID) == true )
           }          // end for ( recID ..
@@ -517,22 +526,10 @@ public:
 
     vlistDestroy(vlistID2);
 
-    cdo_finish();
-
     cdoDebugExt = 0;
   }
 };
 
-void *
-Selmulti(void *process)
-{
-  ModuleSelmulti selmulti;
-  selmulti.init(process);
-  selmulti.run();
-  selmulti.close();
-  return nullptr;
-}
-
 TUPLEREC *
 TUPLERECNew()
 {
@@ -608,7 +605,7 @@ static char *
 removeSpaces(char *pline)
 {
   if (pline == nullptr) return nullptr;
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
   return pline;
 }
 
@@ -616,9 +613,9 @@ static char *
 skipSeparator(char *pline)
 {
   if (pline == nullptr) return nullptr;
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
   if (*pline == '=' || *pline == ':' || *pline == '/' || *pline == ',') pline++;
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
   return pline;
 }
 
@@ -627,7 +624,7 @@ goToNextSeparator(char *pline)
 {
   if (pline == nullptr) return nullptr;
   int separatorFound = 0;
-  while (isspace((int) *pline) || !separatorFound)
+  while (std::isspace((int) *pline) || !separatorFound)
     {
       if (*pline == '\0') return nullptr;
       pline++;
@@ -643,7 +640,7 @@ goToNextSeparator(char *pline)
     }
   if (separatorFound) pline++;
   Debug(cdoDebugExt >= 100, "goToNextSeparator():  pline= ('%s') ", pline);
-  // while ( isspace((int) *pline) ) pline++;
+  // while ( std::isspace((int) *pline) ) pline++;
   pline = removeSpaces(pline);
   return pline;
 }
@@ -725,12 +722,17 @@ findTupleEnd(char *str)
 }
 
 static char *
-readlineForParsing(FILE *gfp, char *strToParsePtr, char *line)
+readlineForParsing(std::ifstream &gfile, char *strToParsePtr, char *line)
 {
-  if (gfp != nullptr)  // file is open => we parse text from a file
+  if (gfile.is_open())  // file is open => we parse text from a file
     {
-      int status = cdo::readline(gfp, line, MAX_LINE_LEN);
-      return (status == 0) ? nullptr : line;
+      std::string strLine;
+      if (std::getline(gfile, strLine))
+        {
+          strcpy(line, strLine.c_str());
+          return line;
+        }
+      else { return nullptr; }
     }
   else if (strToParsePtr != nullptr)  // we parse a given string
     {
@@ -761,14 +763,13 @@ readlineForParsing(FILE *gfp, char *strToParsePtr, char *line)
 int
 multiSelectionParser(const char *filenameOrString)
 {
-  char line[MAX_LINE_LEN], *pline;
+  std::ifstream gfile;
   char *strpos;
   char *parEnd;
   int val;
   float floatval;
   TUPLEREC *tuplerec;
   int selectionRec;
-  FILE *gfp = nullptr;
   char strToParse[MAX_LINE_LEN];
   char *strToParsePtr = nullptr;
   strToParse[0] = 0;
@@ -793,22 +794,23 @@ multiSelectionParser(const char *filenameOrString)
     }
   else
     {
-      gfp = std::fopen(filenameOrString, "r");
+      gfile.open(filenameOrString);
       Debug(cdoDebugExt, " Parsing file:  %s", filenameOrString);
-      if (gfp == nullptr)
+      if (!gfile.is_open())
         {
           cdo_abort(" Missing file:  %s", filenameOrString);
           return 0;
         }
     }
 
-  while ((strToParsePtr = readlineForParsing(gfp, strToParsePtr, line)))
+  char line[MAX_LINE_LEN];
+  while ((strToParsePtr = readlineForParsing(gfile, strToParsePtr, line)))
     {
       if (line[0] == '#') continue;
       if (line[0] == '\0') continue;
-      pline = line;
+      char *pline = line;
       if (cdoDebugExt >= 30) cdo_print(": Line: %s", pline);
-      while (isspace((int) *pline)) pline++;
+      while (std::isspace((int) *pline)) pline++;
       if (pline[0] == '\0') continue;
       strpos = strContains(pline, "SELECT, ");
       selectionRec = 0;  // default is 0;  sel_or_del_or_change:  0:  operator
@@ -1166,8 +1168,9 @@ multiSelectionParser(const char *filenameOrString)
 
             }  // end while pline
         }      // end if "("
-    }          // end while ( cdo::readline(gfp, line, MAX_LINE_LEN) )
-  if (gfp != nullptr) std::fclose(gfp);
+    }          // end while ( readlineForParsing() )
+
+  if (gfile.is_open()) gfile.close();
 
   printSelectionTuples();
   return 1;
@@ -1191,7 +1194,7 @@ printSelectionTuples()
                   tuplerec->nlevels);
       for (ri = 0; ri < tuplerec->ncodes; ri++)
         {
-          sprintf(bff, "%d", tuplerec->codeLST[ri]);
+          std::snprintf(bff, sizeof(bff), "%d", tuplerec->codeLST[ri]);
           strcat(strval, bff);
           if ((ri + 1) < tuplerec->ncodes)
             strcat(strval, "/");
@@ -1200,7 +1203,7 @@ printSelectionTuples()
         }
       for (ri = 0; ri < tuplerec->nlevelTypes; ri++)
         {
-          sprintf(bff, "%d", tuplerec->levelTypeLST[ri]);
+          std::snprintf(bff, sizeof(bff), "%d", tuplerec->levelTypeLST[ri]);
           strcat(strval, bff);
           if ((ri + 1) < tuplerec->nlevelTypes)
             strcat(strval, "/");
@@ -1209,7 +1212,7 @@ printSelectionTuples()
         }
       for (ri = 0; ri < tuplerec->nlevels; ri++)
         {
-          sprintf(bff, "%d", tuplerec->levelLST[ri]);
+          std::snprintf(bff, sizeof(bff), "%d", tuplerec->levelLST[ri]);
           strcat(strval, bff);
           if ((ri + 1) < tuplerec->nlevels)
             strcat(strval, "/");
@@ -1219,7 +1222,7 @@ printSelectionTuples()
 
       if (tuplerec->simpleMath)
         {
-          sprintf(bff, " {scale = %f; offset = %f}", tuplerec->scale, tuplerec->offset);
+          std::snprintf(bff, sizeof(bff), " {scale = %f; offset = %f}", tuplerec->scale, tuplerec->offset);
           strcat(strval, bff);
         }
 
diff --git a/src/Seloperator.cc b/src/Seloperator.cc
index 5a90c928c776cc80fc0a03ec29a0b30c3f75c3ad..a0f5e3cbf5efc514f151797343a4f24af6e9a77b 100644
--- a/src/Seloperator.cc
+++ b/src/Seloperator.cc
@@ -12,8 +12,19 @@
 #include "param_conversion.h"
 #include "cdi_lockedIO.h"
 
-class ModuleSeloperator
+class Seloperator : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Seloperator",
+    .operators = { { "seloperator"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Seloperator> registration = RegisterEntry<Seloperator>(module);
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -33,12 +44,10 @@ class ModuleSeloperator
 
 public:
   void
-  init(void *process)
+  init()
   {
     bool selfound = false;
 
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
     operator_input_arg("code, ltype, level");
@@ -133,17 +142,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Seloperator(void *process)
-{
-  ModuleSeloperator seloperator;
-  seloperator.init(process);
-  seloperator.run();
-  seloperator.close();
-  return nullptr;
-}
diff --git a/src/Selrec.cc b/src/Selrec.cc
index 906a7844856e5542c98d762f9977e35f868eff9d..4db1886229bcc372b1e21118cfd5938f232b4419 100644
--- a/src/Selrec.cc
+++ b/src/Selrec.cc
@@ -17,8 +17,19 @@
 #include "process_int.h"
 #include "param_conversion.h"
 
-class ModuleSelrec
+class Selrec : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selrec",
+    .operators = { { "selrec", SelvarHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, OnlyFirst },
+  };
+  inline static RegisterEntry<Selrec> registration = RegisterEntry<Selrec>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -30,11 +41,8 @@ class ModuleSelrec
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
 
     operator_input_arg("records");
 
@@ -104,17 +112,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Selrec(void *process)
-{
-  ModuleSelrec selrec;
-  selrec.init(process);
-  selrec.run();
-  selrec.close();
-  return nullptr;
-}
diff --git a/src/Selregion.cc b/src/Selregion.cc
index 357711d71b5140fd8eb5c411ea8af0e501f7b469..0f7f077304537fd192d3d3fa6ab6428a82f91975 100644
--- a/src/Selregion.cc
+++ b/src/Selregion.cc
@@ -119,9 +119,9 @@ generate_region_grid(int gridID1, long &gridsize2, std::vector<long> &cellidx, i
   for (int i = 0; i < numFiles; ++i)
     {
       Regions regions;
-      auto param = cdo_operator_argv(i).c_str();
-      if (std::strncmp(param, "dcw:", 4) == 0)
-        read_regions_from_dcw(param + 4, regions);
+      auto param = cdo_operator_argv(i);
+      if (param.starts_with("dcw:"))
+        read_regions_from_dcw(param.c_str() + 4, regions);
       else
         read_regions_from_file(param, regions);
 
@@ -206,7 +206,7 @@ selcircle_get_parameter(CirclePoint &cpoint)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -227,8 +227,22 @@ selcircle_get_parameter(CirclePoint &cpoint)
     }
 }
 
-class ModuleSelregion
+class Selregion : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selregion",
+    .operators = { { "selregion", 0, 0, "DCW region or the path to region file", SelregionHelp },
+                   { "selcircle", 0, 0, "Longitude, latitude of the center and radius of the circle", SelregionHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Selregion>(module);
+
+  int SELREGION, SELCIRCLE;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -244,12 +258,10 @@ class ModuleSelregion
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    auto SELREGION = cdo_operator_add("selregion", 0, 0, "DCW region or the path to region file");
-    auto SELCIRCLE = cdo_operator_add("selcircle", 0, 0, "Longitude, latitude of the center and radius of the circle");
+    SELREGION = module.get_id("selregion");
+    SELCIRCLE = module.get_id("selcircle");
 
     auto operatorID = cdo_operator_id();
 
@@ -369,7 +381,7 @@ public:
                 field2.init(varList2[varID]);
                 window_cell(field1, field2, regions[index].cellidx);
 
-                if (field1.nmiss) field_num_mv(field2);
+                if (field1.numMissVals) field_num_mv(field2);
 
                 cdo_write_record(streamID2, field2);
               }
@@ -385,18 +397,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Selregion(void *process)
-{
-  ModuleSelregion selregion;
-  selregion.init(process);
-  selregion.run();
-  selregion.close();
-
-  return nullptr;
-}
diff --git a/src/Selsurface.cc b/src/Selsurface.cc
index 7dbaa2851f5582e9d425fff6a8bc65ce3d78ebe2..40eea4883f9d1c7ceb61358635bb72cdc25ebabf 100644
--- a/src/Selsurface.cc
+++ b/src/Selsurface.cc
@@ -16,7 +16,7 @@
 
 template <typename T>
 static void
-isosurface_kernel(double isoval, size_t nmiss, const Varray<double> &levels, int nlevels, size_t gridsize, T missval,
+isosurface_kernel(double isoval, size_t numMissVals, const Varray<double> &levels, int nlevels, size_t gridsize, T missval,
                   const Varray<const T *> &data3D, Varray<T> &data2D)
 {
 #ifdef _OPENMP
@@ -31,7 +31,7 @@ isosurface_kernel(double isoval, size_t nmiss, const Varray<double> &levels, int
           const double val1 = data3D[k][i];
           const double val2 = data3D[k + 1][i];
 
-          if (nmiss)
+          if (numMissVals)
             {
               auto hasMissvals1 = dbl_is_equal(val1, missval);
               auto hasMissvals2 = dbl_is_equal(val2, missval);
@@ -56,20 +56,20 @@ isosurface(double isoval, int nlevels, const Varray<double> &levels, const Field
   auto gridsize = gridInqSize(field3D[0].grid);
   auto missval = field3D[0].missval;
 
-  auto nmiss = field3D[0].nmiss;
-  for (int k = 1; k < nlevels; ++k) nmiss += field3D[k].nmiss;
+  auto numMissVals = field3D[0].numMissVals;
+  for (int k = 1; k < nlevels; ++k) numMissVals += field3D[k].numMissVals;
 
   if (field3D[0].memType == MemType::Float)
     {
       Varray<const float *> data3D(nlevels);
       for (int k = 0; k < nlevels; ++k) data3D[k] = field3D[k].vec_f.data();
-      isosurface_kernel(isoval, nmiss, levels, nlevels, gridsize, (float) missval, data3D, field2D.vec_f);
+      isosurface_kernel(isoval, numMissVals, levels, nlevels, gridsize, (float) missval, data3D, field2D.vec_f);
     }
   else
     {
       Varray<const double *> data3D(nlevels);
       for (int k = 0; k < nlevels; ++k) data3D[k] = field3D[k].vec_d.data();
-      isosurface_kernel(isoval, nmiss, levels, nlevels, gridsize, missval, data3D, field2D.vec_d);
+      isosurface_kernel(isoval, numMissVals, levels, nlevels, gridsize, missval, data3D, field2D.vec_d);
     }
 
   field_num_mv(field2D);
@@ -165,8 +165,22 @@ layer_value_max(int nlevels, const FieldVector &field3D, Field &field2D)
   field_num_mv(field2D);
 }
 
-class ModuleSelsurface
+class Selsurface : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selsurface",
+    .operators = { { "isosurface", SelsurfaceHelp },
+                   { "bottomvalue", SelsurfaceHelp },
+                   { "topvalue", SelsurfaceHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Selsurface> registration = RegisterEntry<Selsurface>(module);
+  int ISOSURFACE, BOTTOMVALUE, TOPVALUE;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -194,18 +208,15 @@ class ModuleSelsurface
   bool isPositive;
   bool isReverse;
 
-  int ISOSURFACE, BOTTOMVALUE, TOPVALUE;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-   ISOSURFACE  = cdo_operator_add("isosurface",  0,  0, nullptr);
-   BOTTOMVALUE = cdo_operator_add("bottomvalue", 0,  0, nullptr);
-   TOPVALUE    = cdo_operator_add("topvalue",    0,  0, nullptr);
+ISOSURFACE = module.get_id("isosurface");
+BOTTOMVALUE = module.get_id("bottomvalue");
+TOPVALUE = module.get_id("topvalue");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -340,16 +351,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Selsurface(void *process)
-{
-  ModuleSelsurface selsurface;
-  selsurface.init(process);
-  selsurface.run();
-  selsurface.close();
-  return nullptr;
-}
diff --git a/src/Seltime.cc b/src/Seltime.cc
index f9f7e93ab2022ee79787b492d9b0bbfa612d8d4d..4e5a687ee4a4ca0251eff095ededc06859820960 100644
--- a/src/Seltime.cc
+++ b/src/Seltime.cc
@@ -19,7 +19,6 @@
       Seltime    selsmon         Select single month
 */
 #include <cassert>
-#include <algorithm>  // sort
 #include <cdi.h>
 
 #include "cdo_options.h"
@@ -160,10 +159,37 @@ cdo_argv_to_int_timestep(const std::vector<std::string> &argv, int ntimesteps)
   return v;
 }
 
-void *Select(void *process);
-
-class ModuleSeltime
+class Seltime : public Process
 {
+  enum
+  {
+    func_time,
+    func_date,
+    func_step,
+    func_datetime
+  };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Seltime",
+    .operators = { { "seltimestep", func_step, 0, "timesteps", SeltimeHelp },
+                   { "selyear", func_date, 0, "years", SeltimeHelp },
+                   { "selseason", func_date, 0, "seasons", SeltimeHelp },
+                   { "selmonth", func_date, 0, "months", SeltimeHelp },
+                   { "selday", func_date, 0, "days", SeltimeHelp },
+                   { "selhour", func_time, 0, "hours", SeltimeHelp },
+                   { "seldate", func_datetime, 0, "startdateandenddate(formatYYYY-MM-DDThh:mm:ss)", SeltimeHelp },
+                   { "seltime", func_time, 0, "times(formathh:mm:ss)", SeltimeHelp },
+                   { "selsmon", func_date, 0, "month[,nts1[,nts2]]", SeltimeHelp } },
+    .aliases = { { "selseas", "selseason" }, { "selmon", "selmonth" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Seltime> registration = RegisterEntry<Seltime>(module);
+
+  int SELTIMESTEP, SELDATE, SELTIME, SELHOUR, SELDAY, SELMONTH, SELYEAR, SELSEASON, SELSMON;
   CdoStreamID streamID1;
   CdoStreamID streamID2 = CDO_STREAM_UNDEF;
 
@@ -202,35 +228,21 @@ class ModuleSeltime
   FieldVector3D vars;
   std::vector<bool> selfound;
 
-  int SELTIMESTEP, SELDATE, SELTIME, SELHOUR, SELDAY, SELMONTH, SELYEAR, SELSEASON, SELSMON;
-
-  enum
-  {
-    func_time,
-    func_date,
-    func_step,
-    func_datetime
-  };
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-   SELTIMESTEP = cdo_operator_add("seltimestep", func_step,     0, "timesteps");
-   SELDATE     = cdo_operator_add("seldate",     func_datetime, 0, "start date and end date (format YYYY-MM-DDThh:mm:ss)");
-   SELTIME     = cdo_operator_add("seltime",     func_time,     0, "times (format hh:mm:ss)");
-   SELHOUR     = cdo_operator_add("selhour",     func_time,     0, "hours");
-   SELDAY      = cdo_operator_add("selday",      func_date,     0, "days");
-   SELMONTH    = cdo_operator_add("selmonth",    func_date,     0, "months");
-   SELYEAR     = cdo_operator_add("selyear",     func_date,     0, "years");
-   SELSEASON   = cdo_operator_add("selseason",   func_date,     0, "seasons");
-   SELSMON     = cdo_operator_add("selsmon",     func_date,     0, "month[,nts1[,nts2]]");
-    // clang-format on
+    SELTIMESTEP = module.get_id("seltimestep");
+    SELDATE = module.get_id("seldate");
+    SELTIME = module.get_id("seltime");
+    SELHOUR = module.get_id("selhour");
+    SELDAY = module.get_id("selday");
+    SELMONTH = module.get_id("selmonth");
+    SELYEAR = module.get_id("selyear");
+    SELSEASON = module.get_id("selseason");
+    SELSMON = module.get_id("selsmon");
 
     operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -245,26 +257,32 @@ public:
     if (redirectEnabled)
       {
         // clang-format off
-      if      (ID == SELTIMESTEP ) { newCommand += "timestep=" + argv; }
-      else if (ID == SELDATE)      { newCommand += "date="     + argv; }
-      //else if(ID == SELTIME)    { newCommand += "time="      + argv; }  // unimplemented
-      else if (ID == SELHOUR)      { newCommand += "hour="     + argv; }
-      else if (ID == SELDAY)       { newCommand += "day="      + argv; }
-      else if (ID == SELMONTH)     { newCommand += "month="    + argv; }
-      else if (ID == SELYEAR)      { newCommand += "year="     + argv; }
-      else if (ID == SELSEASON)    { newCommand += "season="   + argv; }
-      else if (ID == SELSMON)      { newCommand += "month="    + argv; }
-      else                         { redirectFound = false; }
+        if      (ID == SELTIMESTEP ) { newCommand += "timestep=" + argv; }
+        else if (ID == SELDATE)      { newCommand += "date="     + argv; }
+        //else if(ID == SELTIME)    { newCommand += "time="      + argv; }  // unimplemented
+        else if (ID == SELHOUR)      { newCommand += "hour="     + argv; }
+        else if (ID == SELDAY)       { newCommand += "day="      + argv; }
+        else if (ID == SELMONTH)     { newCommand += "month="    + argv; }
+        else if (ID == SELYEAR)      { newCommand += "year="     + argv; }
+        else if (ID == SELSEASON)    { newCommand += "season="   + argv; }
+        else if (ID == SELSMON)      { newCommand += "month="    + argv; }
+        else                         { redirectFound = false; }
         // clang-format on
       }
 
     if (redirectFound && redirectEnabled)
       {
-        Debug(Yellow("Redirecting to %s"), newCommand);
-        ((Process *) process)->init_process("select", { newCommand });
-        Select(process);
-        return;
-        // If a redirect was found the entire process is ended through this return!
+        cdo_abort("Redirecting was disabled and does no longer work");
+        /*
+         * This was a temporary feature and does no longer work with the new module system.
+         * Redirecting has to be implemented in antother way or Select needs to be able to handle this kind of
+         * input.
+         *
+         * Debug(Yellow("Redirecting to %s"), newCommand);
+         * ((Process *) process)->init_process("select", { newCommand });
+         * Select(process);
+         * return;
+         * // If a redirect was found the entire process is ended through this return! */
       }
 
     if (operatorID == SELSEASON)
@@ -352,7 +370,7 @@ public:
 
     if (operatorID == SELTIMESTEP)
       {
-        std::sort(intarr.begin(), intarr.end());
+        ranges::sort(intarr);
         auto ip = std::unique(intarr.begin(), intarr.end());
         intarr.resize(std::distance(intarr.begin(), ip));
         numSel = intarr.size();
@@ -536,8 +554,8 @@ public:
                           {
                             cdo_def_record(streamID2, varID, levelID);
                             auto single = vars[it][varID][levelID].vec_d.data();
-                            auto nmiss = vars[it][varID][levelID].nmiss;
-                            cdo_write_record(streamID2, single, nmiss);
+                            auto numMissVals = vars[it][varID][levelID].numMissVals;
+                            cdo_write_record(streamID2, single, numMissVals);
                           }
                       }
                   }
@@ -567,8 +585,8 @@ public:
                           {
                             cdo_def_record(streamID2, varID, levelID);
                             auto single = vars[nts][varID][levelID].vec_d.data();
-                            auto nmiss = vars[nts][varID][levelID].nmiss;
-                            cdo_write_record(streamID2, single, nmiss);
+                            auto numMissVals = vars[nts][varID][levelID].numMissVals;
+                            cdo_write_record(streamID2, single, numMissVals);
                           }
                       }
                   }
@@ -612,7 +630,7 @@ public:
                               for (levelID = 0; levelID < var.nlevels; ++levelID)
                                 {
                                   vars[it][varID][levelID].vec_d = vars[it + 1][varID][levelID].vec_d;
-                                  vars[it][varID][levelID].nmiss = vars[it + 1][varID][levelID].nmiss;
+                                  vars[it][varID][levelID].numMissVals = vars[it + 1][varID][levelID].numMissVals;
                                 }
                             }
                         }
@@ -629,7 +647,7 @@ public:
                     if (lnts1 || var.isConstant)
                       {
                         auto single = vars[nts][varID][levelID].vec_d.data();
-                        cdo_read_record(streamID1, single, &vars[nts][varID][levelID].nmiss);
+                        cdo_read_record(streamID1, single, &vars[nts][varID][levelID].numMissVals);
                       }
                   }
               }
@@ -694,17 +712,5 @@ public:
   close()
   {
     vlistDestroy(vlistID2);
-    cdo_finish();
   }
 };
-
-void *
-Seltime(void *process)
-{
-  ModuleSeltime seltime;
-  seltime.init(process);
-  seltime.run();
-  seltime.close();
-
-  return nullptr;
-}
diff --git a/src/Selvar.cc b/src/Selvar.cc
index 0dcf3cf9ed3ef89e465a7b2f40932df26770970a..f52d0ed9c7a9bd34acdbac730073efe13369c778 100644
--- a/src/Selvar.cc
+++ b/src/Selvar.cc
@@ -33,8 +33,35 @@
 #include "cdi_lockedIO.h"
 #include "param_conversion.h"
 
-class ModuleSelvar
+class Selvar : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selvar",
+    .operators = { { "selparam", 0, 2, "parameters", SelvarHelp },
+                   { "selcode", 0, 4, "code numbers", SelvarHelp },
+                   { "selname", 0, 2, "variable names", SelvarHelp },
+                   { "selstdname", 0, 2, "standard names", SelvarHelp },
+                   { "sellevel", 0, 8, "levels", SelvarHelp },
+                   { "sellevidx", 0, 4, "index of levels", SelvarHelp },
+                   { "selgrid", 0, 4 | 2, "list of grid names or numbers", SelvarHelp },
+                   { "selzaxis", 0, 4 | 2, "list of zaxis types or numbers", SelvarHelp },
+                   { "selzaxisname", 0, 2, "list of zaxis names", SelvarHelp },
+                   { "seltabnum", 0, 4, "table numbers", SelvarHelp },
+                   { "delparam", 1, 2 | 1, "parameter", SelvarHelp },
+                   { "delcode", 1, 1, "code numbers", SelvarHelp },
+                   { "delname", 1, 2 | 1, "variable names", SelvarHelp },
+                   { "selltype", 0, 4, "GRIB level types", SelvarHelp } },
+    .aliases = { { "selvar", "selname" }, { "delvar", "delname" }, { "selgridname", "selgrid" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Selvar> registration = RegisterEntry<Selvar>(module);
+
+  int SELPARAM, SELCODE, SELNAME, SELSTDNAME, SELLEVEL, SELLEVIDX, SELGRID, SELZAXIS, SELZAXISNAME, SELTABNUM, DELPARAM, DELCODE,
+      DELNAME, SELLTYPE;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -50,12 +77,9 @@ class ModuleSelvar
 
   bool dataIsUnchanged;
 
-  int SELPARAM, SELCODE, SELNAME, SELSTDNAME, SELLEVEL, SELLEVIDX, SELGRID, SELZAXIS, SELZAXISNAME, SELTABNUM, DELPARAM, DELCODE,
-      DELNAME, SELLTYPE;
-
 public:
   void
-  init(void *process)
+  init()
   {
     int nsel = 0;
     char paramstr[32];
@@ -64,8 +88,6 @@ public:
     std::vector<int> intarr;
     std::vector<double> fltarr;
 
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
 #define INVERTS_SELECTION(id) (cdo_operator_f2(id) & 1)
@@ -73,22 +95,20 @@ public:
 #define TAKES_INTEGERS(id) (cdo_operator_f2(id) & 4)
 #define TAKES_FLOATS(id) (cdo_operator_f2(id) & 8)
 
-    // clang-format off
-    SELPARAM     = cdo_operator_add("selparam",     0, 2,   "parameters");
-    SELCODE      = cdo_operator_add("selcode",      0, 4,   "code numbers");
-    SELNAME      = cdo_operator_add("selname",      0, 2,   "variable names");
-    SELSTDNAME   = cdo_operator_add("selstdname",   0, 2,   "standard names");
-    SELLEVEL     = cdo_operator_add("sellevel",     0, 8,   "levels");
-    SELLEVIDX    = cdo_operator_add("sellevidx",    0, 4,   "index of levels");
-    SELGRID      = cdo_operator_add("selgrid",      0, 4|2, "list of grid names or numbers");
-    SELZAXIS     = cdo_operator_add("selzaxis",     0, 4|2, "list of zaxis types or numbers");
-    SELZAXISNAME = cdo_operator_add("selzaxisname", 0, 2,   "list of zaxis names");
-    SELTABNUM    = cdo_operator_add("seltabnum",    0, 4,   "table numbers");
-    DELPARAM     = cdo_operator_add("delparam",     1, 2|1, "parameter");
-    DELCODE      = cdo_operator_add("delcode",      1, 1,   "code numbers");
-    DELNAME      = cdo_operator_add("delname",      1, 2|1, "variable names");
-    SELLTYPE     = cdo_operator_add("selltype",     0, 4,   "GRIB level types");
-    // clang-format on
+    SELPARAM = module.get_id("selparam");
+    SELCODE = module.get_id("selcode");
+    SELNAME = module.get_id("selname");
+    SELSTDNAME = module.get_id("selstdname");
+    SELLEVEL = module.get_id("sellevel");
+    SELLEVIDX = module.get_id("sellevidx");
+    SELGRID = module.get_id("selgrid");
+    SELZAXIS = module.get_id("selzaxis");
+    SELZAXISNAME = module.get_id("selzaxisname");
+    SELTABNUM = module.get_id("seltabnum");
+    DELPARAM = module.get_id("delparam");
+    DELCODE = module.get_id("delcode");
+    DELNAME = module.get_id("delname");
+    SELLTYPE = module.get_id("selltype");
 
     auto operatorID = cdo_operator_id();
 
@@ -284,10 +304,10 @@ public:
 
     if (npar == 0) cdo_abort("No variables selected!");
 
-     vlistID2 = vlistCreate();
+    vlistID2 = vlistCreate();
     cdo_vlist_copy_flag(vlistID2, vlistID1);
 
-    if (Options::cdoVerbose) vlistPrint(vlistID2);
+    // if (Options::cdoVerbose) vlistPrint(vlistID2);
 
     nvars = vlistNvars(vlistID2);
     {
@@ -297,13 +317,14 @@ public:
       if (varID == nvars) vlistDefNtsteps(vlistID2, 0);
     }
 
-     taxisID1 = vlistInqTaxis(vlistID1);
-     taxisID2 = taxisDuplicate(taxisID1);
+    taxisID1 = vlistInqTaxis(vlistID1);
+    taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-     streamID2 = cdo_open_write(1);
+    streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
   run()
   {
@@ -348,17 +369,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Selvar(void *process)
-{
-  ModuleSelvar selvar;
-  selvar.init(process);
-  selvar.run();
-  selvar.close();
-  return nullptr;
-}
diff --git a/src/Selyearidx.cc b/src/Selyearidx.cc
index 435133548c0f0c9b54ea5e01e6ca635cbe1c9ecf..52e477a13a572c65a043d2eae5136485e578a99f 100644
--- a/src/Selyearidx.cc
+++ b/src/Selyearidx.cc
@@ -17,8 +17,19 @@
 #include "cdo_vlist.h"
 #include "field_functions.h"
 
-class ModuleSelyearidx
+class Selyearidx : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Selyearidx",
+    .operators = { { "selyearidx", SelyearidxHelp }, { "seltimeidx", 1, 0, SelyearidxHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Selyearidx> registration = RegisterEntry<Selyearidx>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -38,12 +49,8 @@ class ModuleSelyearidx
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("selyearidx", 0, 0, nullptr);
-    cdo_operator_add("seltimeidx", 1, 0, nullptr);
 
     ltime = cdo_operator_f1(cdo_operator_id());
 
@@ -110,9 +117,9 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, vars1[varID][levelID].vec_d.data(), &nmiss);
-            vars1[varID][levelID].nmiss = nmiss;
+            size_t numMissVals;
+            cdo_read_record(streamID1, vars1[varID][levelID].vec_d.data(), &numMissVals);
+            vars1[varID][levelID].numMissVals = numMissVals;
 
             if (tsID == 0) recList[recID].set(varID, levelID);
           }
@@ -129,8 +136,8 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID2, &varID, &levelID);
-                size_t nmiss;
-                cdo_read_record(streamID2, array.data(), &nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID2, array.data(), &numMissVals);
 
                 const auto &var = varList2[varID];
                 for (size_t i = 0; i < var.gridsize; ++i)
@@ -175,24 +182,11 @@ public:
       }
   }
 
-void
+  void
   close()
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Selyearidx(void *process)
-{
-  ModuleSelyearidx selyearidx;
-  selyearidx.init(process);
-  selyearidx.run();
-  selyearidx.close();
-
-  return nullptr;
-}
diff --git a/src/Set.cc b/src/Set.cc
index 8395813dfe0f1c421f755375d556e4e39996b1c6..86845c5aa8e23dd939b61ebdaca614ac9fba008a 100644
--- a/src/Set.cc
+++ b/src/Set.cc
@@ -25,12 +25,12 @@
 static void
 set_level(int vlistID2, double newlevel)
 {
-  const auto nzaxis = vlistNzaxis(vlistID2);
+  auto nzaxis = vlistNzaxis(vlistID2);
   for (int index = 0; index < nzaxis; ++index)
     {
-      const auto zaxisID1 = vlistZaxis(vlistID2, index);
-      const auto zaxisID2 = zaxisDuplicate(zaxisID1);
-      const auto nlevs = zaxisInqSize(zaxisID2);
+      auto zaxisID1 = vlistZaxis(vlistID2, index);
+      auto zaxisID2 = zaxisDuplicate(zaxisID1);
+      auto nlevs = zaxisInqSize(zaxisID2);
       Varray<double> levels(nlevs);
       cdo_zaxis_inq_levels(zaxisID2, levels.data());
       levels[0] = newlevel;
@@ -42,20 +42,40 @@ set_level(int vlistID2, double newlevel)
 static void
 set_ltype(int vlistID2, double newval)
 {
-  const auto nzaxis = vlistNzaxis(vlistID2);
+  auto nzaxis = vlistNzaxis(vlistID2);
   for (int index = 0; index < nzaxis; ++index)
     {
-      const auto zaxisID1 = vlistZaxis(vlistID2, index);
-      const auto zaxisID2 = zaxisDuplicate(zaxisID1);
-      const auto zaxistype = ZAXIS_GENERIC;
+      auto zaxisID1 = vlistZaxis(vlistID2, index);
+      auto zaxisID2 = zaxisDuplicate(zaxisID1);
+      auto zaxistype = ZAXIS_GENERIC;
       zaxisChangeType(zaxisID2, zaxistype);
       cdiDefKeyInt(zaxisID2, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, newval);
       vlistChangeZaxis(vlistID2, zaxisID1, zaxisID2);
     }
 }
 
-class ModuleSet
+class Set : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Set",
+    .operators = { { "setcode", 0, 0, "code number", SetHelp },
+                   { "setparam", 0, 0, "parameter identifier (format:code[.tabnum]ornum[.cat[.dis]])", SetHelp },
+                   { "setname", 0, 0, "variable name", SetHelp },
+                   { "setunit", 0, 0, "variable unit", SetHelp },
+                   { "setlevel", 0, 0, "level", SetHelp },
+                   { "setltype", 0, 0, "GRIB level type", SetHelp },
+                   { "settabnum", 0, 0, "GRIB table number", SetHelp },
+                   { "setmaxsteps", 0, 0, "max. number of timesteps", SetHelp } },
+    .aliases = { { "setvar", "setname" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Set> registration = RegisterEntry<Set>(module);
+
+  int SETCODE, SETPARAM, SETNAME, SETUNIT, SETLEVEL, SETLTYPE, SETTABNUM, SETMAXSTEPS;
   int maxSteps = -1;
   int newval = -1, tabnum = 0;
   int newparam = 0;
@@ -73,22 +93,18 @@ class ModuleSet
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    const auto SETCODE     = cdo_operator_add("setcode",     0, 0, "code number");
-    const auto SETPARAM    = cdo_operator_add("setparam",    0, 0, "parameter identifier (format: code[.tabnum] or num[.cat[.dis]])");
-    const auto SETNAME     = cdo_operator_add("setname",     0, 0, "variable name");
-    const auto SETUNIT     = cdo_operator_add("setunit",     0, 0, "variable unit");
-    const auto SETLEVEL    = cdo_operator_add("setlevel",    0, 0, "level");
-    const auto SETLTYPE    = cdo_operator_add("setltype",    0, 0, "GRIB level type");
-    const auto SETTABNUM   = cdo_operator_add("settabnum",   0, 0, "GRIB table number");
-    const auto SETMAXSTEPS = cdo_operator_add("setmaxsteps", 0, 0, "max. number of timesteps");
-    // clang-format on
+    SETCODE = module.get_id("setcode");
+    SETPARAM = module.get_id("setparam");
+    SETNAME = module.get_id("setname");
+    SETUNIT = module.get_id("setunit");
+    SETLEVEL = module.get_id("setlevel");
+    SETLTYPE = module.get_id("setltype");
+    SETTABNUM = module.get_id("settabnum");
+    SETMAXSTEPS = module.get_id("setmaxsteps");
 
-    const auto operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
 
     operator_input_arg(cdo_operator_enter(operatorID));
     if (operatorID == SETCODE || operatorID == SETLTYPE) { newval = parameter_to_int(cdo_operator_argv(0)); }
@@ -101,8 +117,8 @@ public:
 
     streamID1 = cdo_open_read(0);
 
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-    const auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
     // vlistPrint(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
@@ -111,7 +127,7 @@ public:
 
     if (operatorID == SETCODE)
       {
-        const auto nvars = vlistNvars(vlistID2);
+        auto nvars = vlistNvars(vlistID2);
         for (int varID = 0; varID < nvars; ++varID) vlistDefVarCode(vlistID2, varID, newval);
       }
     else if (operatorID == SETPARAM) { vlistDefVarParam(vlistID2, 0, newparam); }
@@ -119,8 +135,8 @@ public:
     else if (operatorID == SETUNIT) { cdiDefKeyString(vlistID2, 0, CDI_KEY_UNITS, newunit); }
     else if (operatorID == SETTABNUM)
       {
-        const auto tableID = tableDef(-1, tabnum, nullptr);
-        const auto nvars = vlistNvars(vlistID2);
+        auto tableID = tableDef(-1, tabnum, nullptr);
+        auto nvars = vlistNvars(vlistID2);
         for (int varID = 0; varID < nvars; ++varID) vlistDefVarTable(vlistID2, varID, tableID);
       }
     else if (operatorID == SETLEVEL) { set_level(vlistID2, newlevel); }
@@ -138,7 +154,7 @@ public:
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -163,17 +179,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Set(void *process)
-{
-  ModuleSet set;
-  set.init(process);
-  set.run();
-  set.close();
-  return nullptr;
-}
diff --git a/src/Setattribute.cc b/src/Setattribute.cc
index 37f3a1c2fc61b8dc362c3e7c6979df93b36c7993..afbea5dd8357a01a3ff947b14189e2a8569e5e18 100644
--- a/src/Setattribute.cc
+++ b/src/Setattribute.cc
@@ -189,9 +189,7 @@ find_attribute(int cdiID, int varID, const std::string &attrName, int &dtype)
       auto longname = cdo::inq_var_longname(cdiID, varID);
       auto units = cdo::inq_var_units(cdiID, varID);
 
-      auto param = vlistInqVarParam(cdiID, varID);
-      char paramstr[32];
-      param_to_string(param, paramstr, sizeof(paramstr));
+      auto paramstr = param_to_string(vlistInqVarParam(cdiID, varID));
 
       auto code = vlistInqVarCode(cdiID, varID);
       auto table = vlistInqVarTable(cdiID, varID);
@@ -349,8 +347,20 @@ set_attributes(const KVList &kvlist, int vlistID)
     }
 }
 
-class ModuleSetattribute
+class Setattribute : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setattribute",
+    .operators = { { "setattribute", 0, 0, "attributes", SetattributeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Setattribute> registration = RegisterEntry<Setattribute>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -364,12 +374,8 @@ class ModuleSetattribute
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("setattribute", 0, 0, "attributes");
-
     dataIsUnchanged = data_is_unchanged();
 
     auto operatorID = cdo_operator_id();
@@ -382,7 +388,7 @@ public:
     PMList pmlist;
     KVList kvlist;
     kvlist.name = cdo_module_name();
-    if (kvlist.parse_arguments(natts, cdo_get_oper_argv()) != 0) cdo_abort("Parse error!");
+    if (kvlist.parse_arguments(cdo_get_oper_argv()) != 0) cdo_abort("Parse error!");
     if (Options::cdoVerbose) kvlist.print();
 
     auto pkvlist = &kvlist;
@@ -455,17 +461,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-    cdo_finish();
   }
 };
-
-void *
-Setattribute(void *process)
-{
-  ModuleSetattribute setattribute;
-  setattribute.init(process);
-  setattribute.run();
-  setattribute.close();
-
-  return nullptr;
-}
diff --git a/src/Setbox.cc b/src/Setbox.cc
index 790e874cdee2ab1a3d1884aa773cc2c2fdac37d9..5ef49deac3d1440e0a822606e9633de2a32f497d 100644
--- a/src/Setbox.cc
+++ b/src/Setbox.cc
@@ -28,8 +28,8 @@ setcbox(double constant, double *array, int gridID, const SelboxInfo &selboxInfo
   const auto &lon12 = selboxInfo.lon12;
   const auto &lon21 = selboxInfo.lon21;
   const auto &lon22 = selboxInfo.lon22;
-  const long nlon = gridInqXsize(gridID);
-  const long nlat = gridInqYsize(gridID);
+  long nlon = gridInqXsize(gridID);
+  long nlat = gridInqYsize(gridID);
 
   for (long ilat = 0; ilat < nlat; ilat++)
     for (long ilon = 0; ilon < nlon; ilon++)
@@ -44,17 +44,17 @@ get_gridID(int vlistID1, bool operIndexBox)
 {
   std::vector<int> gridsFound;
 
-  const auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNgrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
-      const auto gridID1 = vlistGrid(vlistID1, index);
+      auto gridID1 = vlistGrid(vlistID1, index);
       if (gridInqSize(gridID1) == 1) continue;
 
-      const auto gridtype = gridInqType(gridID1);
-      const auto projtype = gridInqProjType(gridID1);
+      auto gridtype = gridInqType(gridID1);
+      auto projtype = gridInqProjType(gridID1);
 
-      const auto isReg2dGeoGrid = (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_CURVILINEAR);
-      const auto projHasGeoCoords = (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_RLL);
+      auto isReg2dGeoGrid = (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_CURVILINEAR);
+      auto projHasGeoCoords = (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_RLL);
 
       if (isReg2dGeoGrid || projHasGeoCoords || (operIndexBox && (gridtype == GRID_GENERIC || gridtype == GRID_PROJECTION)))
         {
@@ -69,14 +69,14 @@ get_gridID(int vlistID1, bool operIndexBox)
   if (gridsFound.size() == 0) cdo_abort("No processable grid found!");
   if (gridsFound.size() > 1) cdo_abort("Too many different grids!");
 
-  const auto gridID = gridsFound[0];
+  auto gridID = gridsFound[0];
   return gridID;
 }
 
 static std::vector<bool>
 get_processVars(int vlistID1, int gridID)
 {
-  const auto nvars = vlistNvars(vlistID1);
+  auto nvars = vlistNvars(vlistID1);
 
   std::vector<bool> processVars(nvars, false);
 
@@ -92,8 +92,22 @@ get_processVars(int vlistID1, int gridID)
   return processVars;
 }
 
-class ModuleSetbox
+class Setbox : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setbox",
+    .operators = { { "setclonlatbox", 0, 0, "constant, western and eastern longitude and southern and northern latitude", SetboxHelp },
+                   { "setcindexbox", 0, 0, "constant, index of first and last longitude and index of first and last latitude", SetboxHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Setbox> registration = RegisterEntry<Setbox>(module);
+
+  int SETCLONLATBOX, SETCINDEXBOX;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -109,24 +123,19 @@ class ModuleSetbox
 
   Varray<double> array;
   std::vector<bool> processVars;
-  int SETCLONLATBOX, SETCINDEXBOX;
   SelboxInfo selboxInfo;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-   SETCLONLATBOX = cdo_operator_add("setclonlatbox", 0, 0, "constant, western and eastern longitude and southern and northern latitude");
-   SETCINDEXBOX = cdo_operator_add("setcindexbox", 0, 0, "constant, index of first and last longitude and index of first and last latitude");
-    // clang-format on
+    SETCLONLATBOX = module.get_id("setclonlatbox");
+    SETCINDEXBOX = module.get_id("setcindexbox");
 
     (void) SETCLONLATBOX;
 
-    const auto operatorID = cdo_operator_id();
-    const auto operIndexBox = (operatorID == SETCINDEXBOX);
+    auto operatorID = cdo_operator_id();
+    auto operIndexBox = (operatorID == SETCINDEXBOX);
 
     operator_input_arg(cdo_operator_enter(operatorID));
 
@@ -143,7 +152,7 @@ public:
 
     selboxInfo = operIndexBox ? gen_index_selbox(1, gridID) : gen_lonlat_selbox(1, gridID);
 
-    const auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -156,13 +165,14 @@ public:
     gridsize = gridInqSize(gridID);
     array = Varray<double>(gridsize);
   }
+
   void
   run()
   {
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -175,38 +185,26 @@ public:
 
             if (processVars[varID])
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array.data(), &nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID1, array.data(), &numMissVals);
 
                 setcbox(constant, array.data(), gridID, selboxInfo);
 
-                const auto missval = vlistInqVarMissval(vlistID1, varID);
-                nmiss = varray_num_mv(gridsize, array, missval);
+                auto missval = vlistInqVarMissval(vlistID1, varID);
+                numMissVals = varray_num_mv(gridsize, array, missval);
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, array.data(), nmiss);
+                cdo_write_record(streamID2, array.data(), numMissVals);
               }
           }
 
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Setbox(void *process)
-{
-  ModuleSetbox setbox;
-  setbox.init(process);
-  setbox.run();
-  setbox.close();
-
-  return nullptr;
-}
diff --git a/src/Setgrid.cc b/src/Setgrid.cc
index 6c511155f94229d5b17ef4cec377775ea4508819..6a1fb6a8624540cf379bdaebbdf37c5184792205 100644
--- a/src/Setgrid.cc
+++ b/src/Setgrid.cc
@@ -100,6 +100,11 @@ grid_set_type(int vlistID1, int vlistID2, int gridtype, const std::string &gridn
               gridID2 = -1;
               cdo_warning("Conversion of generic grid to regular grid failed!");
             }
+          else if (gridtype == GRID_LONLAT && gridtype1 == GRID_PROJECTION)
+            {
+              gridID2 = gridProjectionToRegular(gridID1);
+              if (gridID2 == -1) cdo_warning("Conversion of projection to regular grid failed!");
+            }
           else if (gridtype == GRID_LONLAT && gridtype1 == GRID_LONLAT) { gridID2 = gridID1; }
           else if (gridtype == GRID_PROJECTION)
             {
@@ -254,8 +259,8 @@ grid_read_cellarea(const std::string &areafile, Varray<double> &gridcellArea)
           auto areasize = gridInqSize(varList[varID].gridID);
           gridcellArea.resize(areasize);
 
-          size_t nmiss;
-          streamReadRecord(streamID, gridcellArea.data(), &nmiss);
+          size_t numMissVals;
+          streamReadRecord(streamID, gridcellArea.data(), &numMissVals);
           break;
         }
     }
@@ -263,8 +268,29 @@ grid_read_cellarea(const std::string &areafile, Varray<double> &gridcellArea)
   streamClose(streamID);
 }
 
-class ModuleSetgrid
+class Setgrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setgrid",
+    .operators = { { "setgrid", 0, 0, "grid description file or name", SetgridHelp },
+                   { "setgridtype", 0, 0, "gridtype", SetgridHelp },
+                   { "setgridarea", 0, 0, "filename with areaweights", SetgridHelp },
+                   { "setgridmask", 0, 0, "filename with gridmask", SetgridHelp },
+                   { "unsetgridmask", SetgridHelp },
+                   { "setgridnumber", 0, 0, "gridnumber and optionally grid position", SetgridHelp },
+                   { "setgriduri", 0, 0, "reference URI of the horizontal grid", SetgridHelp },
+                   { "usegridnumber", 0, 0, "use existing grid identified by gridnumber", SetgridHelp },
+                   { "setprojparams", 0, 0, "proj library parameter (e.g.:+init=EPSG:3413)", SetgridHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Setgrid>(module);
+
+  int SETGRID, SETGRIDTYPE, SETGRIDAREA, SETGRIDMASK, UNSETGRIDMASK, SETGRIDNUMBER, SETGRIDURI, USEGRIDNUMBER, SETPROJPARAMS;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -281,7 +307,7 @@ class ModuleSetgrid
 
 public:
   void
-  init(void *process)
+  init()
   {
     int number = 0, position = 0;
     std::string griduri;
@@ -289,19 +315,15 @@ public:
     std::string gridname;
     std::string gridfile;
 
-    cdo_initialize(process);
-
-    // clang-format off
-    auto SETGRID       = cdo_operator_add("setgrid",       0, 0, "grid description file or name");
-    auto SETGRIDTYPE   = cdo_operator_add("setgridtype",   0, 0, "grid type");
-    auto SETGRIDAREA   = cdo_operator_add("setgridarea",   0, 0, "filename with area weights");
-    auto SETGRIDMASK   = cdo_operator_add("setgridmask",   0, 0, "filename with grid mask");
-    auto UNSETGRIDMASK = cdo_operator_add("unsetgridmask", 0, 0, nullptr);
-    auto SETGRIDNUMBER = cdo_operator_add("setgridnumber", 0, 0, "grid number and optionally grid position");
-    auto SETGRIDURI    = cdo_operator_add("setgriduri",    0, 0, "reference URI of the horizontal grid");
-    auto USEGRIDNUMBER = cdo_operator_add("usegridnumber", 0, 0, "use existing grid identified by grid number");
-    auto SETPROJPARAMS = cdo_operator_add("setprojparams", 0, 0, "proj library parameter (e.g.: +init=EPSG:3413)");
-    // clang-format on
+    SETGRID = module.get_id("setgrid");
+    SETGRIDTYPE = module.get_id("setgridtype");
+    SETGRIDAREA = module.get_id("setgridarea");
+    SETGRIDMASK = module.get_id("setgridmask");
+    UNSETGRIDMASK = module.get_id("unsetgridmask");
+    SETGRIDNUMBER = module.get_id("setgridnumber");
+    SETGRIDURI = module.get_id("setgriduri");
+    USEGRIDNUMBER = module.get_id("usegridnumber");
+    SETPROJPARAMS = module.get_id("setprojparams");
 
     auto operatorID = cdo_operator_id();
 
@@ -346,8 +368,8 @@ public:
         auto masksize = gridInqSize(gridID);
         gridmask.resize(masksize);
 
-        size_t nmiss;
-        streamReadRecord(streamID, gridmask.data(), &nmiss);
+        size_t numMissVals;
+        streamReadRecord(streamID, gridmask.data(), &numMissVals);
         streamClose(streamID);
 
         for (size_t i = 0; i < masksize; ++i)
@@ -482,7 +504,7 @@ public:
 
             field.init((lregular || lregularnn) ? varList2[varID] : varList1[varID]);
             cdo_read_record(streamID1, field);
-            auto nmiss = field.nmiss;
+            auto numMissVals = field.numMissVals;
 
             auto gridID1 = varList1[varID].gridID;
             if (lregular || lregularnn)
@@ -491,7 +513,7 @@ public:
                   {
                     auto missval = varList1[varID].missval;
                     int lnearst = lregularnn ? 1 : 0;
-                    field2regular(gridID1, varList2[varID].gridID, missval, field.vec_d.data(), nmiss, lnearst);
+                    field2regular(gridID1, varList2[varID].gridID, missval, field.vec_d.data(), numMissVals, lnearst);
                   }
               }
             else if (gridInqType(gridID1) == GRID_GME)
@@ -521,17 +543,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Setgrid(void *process)
-{
-  ModuleSetgrid setgrid;
-  setgrid.init(process);
-  setgrid.run();
-  setgrid.close();
-  return nullptr;
-}
diff --git a/src/Setgridcell.cc b/src/Setgridcell.cc
index b95c19295edc2f0075c79777c44b21bcc16fb56d..5e8fa8c8495c40bf01dd72d631cf802db121780e 100644
--- a/src/Setgridcell.cc
+++ b/src/Setgridcell.cc
@@ -31,9 +31,9 @@ static void
 set_value(Field &field, double value)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = set_value(field.size, field.vec_f, (float) field.missval, (float) value);
+    field.numMissVals = set_value(field.size, field.vec_f, (float) field.missval, (float) value);
   else
-    field.nmiss = set_value(field.size, field.vec_d, field.missval, value);
+    field.numMissVals = set_value(field.size, field.vec_d, field.missval, value);
 }
 
 template <typename T>
@@ -54,9 +54,9 @@ static void
 set_value(Field &field, double value, const Varray<size_t> &cells)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = set_value(field.size, field.vec_f, (float) field.missval, (float) value, cells);
+    field.numMissVals = set_value(field.size, field.vec_f, (float) field.missval, (float) value, cells);
   else
-    field.nmiss = set_value(field.size, field.vec_d, field.missval, value, cells);
+    field.numMissVals = set_value(field.size, field.vec_d, field.missval, value, cells);
 }
 
 static void
@@ -88,7 +88,7 @@ setgridcell_get_parameter(double &constant, Varray<size_t> &cells, std::vector<s
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -126,8 +126,19 @@ setgridcell_get_parameter(double &constant, Varray<size_t> &cells, std::vector<s
     }
 }
 
-class ModuleSetgridcell
+class Setgridcell : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setgridcell",
+    .operators = { { "setgridcell", 0, 0, "value=constant[,cell=gridcellindices(1-N)]", SetgridcellHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Setgridcell> registration = RegisterEntry<Setgridcell>(module);
 
   Field field;
 
@@ -149,11 +160,8 @@ class ModuleSetgridcell
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("setgridcell", 0, 0, "value=constant[, cell=grid cell indices (1-N)]");
 
     operator_input_arg(cdo_operator_enter(0));
 
@@ -254,17 +262,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Setgridcell(void *process)
-{
-  ModuleSetgridcell setgridcell;
-  setgridcell.init(process);
-  setgridcell.run();
-  setgridcell.close();
-  return nullptr;
-}
diff --git a/src/Sethalo.cc b/src/Sethalo.cc
index 0f73d78c1dca412efddef360f83774c9fcbf96fd..3957d0118fa6621d9f4caac14ab6296d94b105bb 100644
--- a/src/Sethalo.cc
+++ b/src/Sethalo.cc
@@ -350,7 +350,7 @@ get_parameter(void)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -382,10 +382,7 @@ gen_index_grid(int gridID1, Halo &halo)
       halo.east = parameter_to_long(cdo_operator_argv(0));
       halo.west = parameter_to_long(cdo_operator_argv(1));
     }
-  else
-    {
-      halo = get_parameter();
-    }
+  else { halo = get_parameter(); }
 
   auto isCircularGrid = gridIsCircular(gridID1);
   long nlon = gridInqXsize(gridID1);
@@ -502,8 +499,20 @@ get_gridID(int vlistID1)
   return gridID1;
 }
 
-class ModuleSethalo
+class Sethalo : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Sethalo",
+    .operators = { { "sethalo", SethaloHelp }, { "tpnhalo", SethaloHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Sethalo> registration = RegisterEntry<Sethalo>(module);
+
   int SETHALO;
   int operatorID;
 
@@ -525,14 +534,9 @@ class ModuleSethalo
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    SETHALO = cdo_operator_add("sethalo", 0, 0, nullptr);
-              cdo_operator_add("tpnhalo", 0, 0, nullptr);
-    // clang-format on
+    SETHALO = module.get_id("sethalo");
 
     operatorID = cdo_operator_id();
 
@@ -591,8 +595,8 @@ public:
 
             if (vars[varID])
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array1.data(), &nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID1, array1.data(), &numMissVals);
 
                 auto recalcNumMiss = false;
                 auto missval = varList[varID].missval;
@@ -601,10 +605,10 @@ public:
                 else
                   tripolar_halo(array1, gridID1, array2);
 
-                if (nmiss || recalcNumMiss) nmiss = varray_num_mv(array2.size(), array2, missval);
+                if (numMissVals || recalcNumMiss) numMissVals = varray_num_mv(array2.size(), array2, missval);
 
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, array2.data(), nmiss);
+                cdo_write_record(streamID2, array2.data(), numMissVals);
               }
           }
 
@@ -617,18 +621,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Sethalo(void *process)
-{
-  ModuleSethalo sethalo;
-  sethalo.init(process);
-  sethalo.run();
-  sethalo.close();
-
-  return nullptr;
-}
diff --git a/src/Setmiss.cc b/src/Setmiss.cc
index c4d93fc2871603282d962507e83c4a04cc2dc0d3..aade5cc3617b0acaf5a0d24e48c87c3b4511fed2 100644
--- a/src/Setmiss.cc
+++ b/src/Setmiss.cc
@@ -25,39 +25,39 @@ template <typename T>
 static size_t
 set_missval(size_t gridsize, Varray<T> &array, T missval, T new_missval)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < gridsize; ++i)
     if (DBL_IS_EQUAL(array[i], missval) || DBL_IS_EQUAL(array[i], (float) missval) || DBL_IS_EQUAL(array[i], new_missval)
         || DBL_IS_EQUAL(array[i], (float) new_missval))
       {
         array[i] = new_missval;
-        nmiss++;
+        numMissVals++;
       }
 
-  return nmiss;
+  return numMissVals;
 }
 
 static void
 set_missval(Field &field, double new_missval)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = set_missval(field.size, field.vec_f, (float) field.missval, (float) new_missval);
+    field.numMissVals = set_missval(field.size, field.vec_f, (float) field.missval, (float) new_missval);
   else
-    field.nmiss = set_missval(field.size, field.vec_d, field.missval, new_missval);
+    field.numMissVals = set_missval(field.size, field.vec_d, field.missval, new_missval);
 }
 
 template <typename T>
 static size_t
 set_const_to_miss(size_t gridsize, Varray<T> &array, T missval, T rconst)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   if (std::isnan(rconst))
     {
       for (size_t i = 0; i < gridsize; ++i)
         if (std::isnan(array[i]))
           {
             array[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
   else
@@ -66,20 +66,20 @@ set_const_to_miss(size_t gridsize, Varray<T> &array, T missval, T rconst)
         if (DBL_IS_EQUAL(array[i], rconst) || DBL_IS_EQUAL(array[i], (float) rconst))
           {
             array[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 static void
 set_const_to_miss(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    field.nmiss += set_const_to_miss(field.size, field.vec_f, (float) field.missval, (float) rconst);
+    field.numMissVals += set_const_to_miss(field.size, field.vec_f, (float) field.missval, (float) rconst);
   else
-    field.nmiss += set_const_to_miss(field.size, field.vec_d, field.missval, rconst);
+    field.numMissVals += set_const_to_miss(field.size, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
@@ -96,33 +96,33 @@ static void
 set_miss_to_const(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = set_miss_to_const(field.size, field.vec_f, (float) field.missval, (float) rconst);
+    field.numMissVals = set_miss_to_const(field.size, field.vec_f, (float) field.missval, (float) rconst);
   else
-    field.nmiss = set_miss_to_const(field.size, field.vec_d, field.missval, rconst);
+    field.numMissVals = set_miss_to_const(field.size, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static size_t
 set_range_to_miss(size_t gridsize, Varray<T> &array, T missval, T rmin, T rmax)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < gridsize; ++i)
     if (array[i] >= rmin && array[i] <= rmax)
       {
         array[i] = missval;
-        nmiss++;
+        numMissVals++;
       }
 
-  return nmiss;
+  return numMissVals;
 }
 
 static void
 set_range_to_miss(Field &field, double rmin, double rmax)
 {
   if (field.memType == MemType::Float)
-    field.nmiss += set_range_to_miss(field.size, field.vec_f, (float) field.missval, (float) rmin, (float) rmax);
+    field.numMissVals += set_range_to_miss(field.size, field.vec_f, (float) field.missval, (float) rmin, (float) rmax);
   else
-    field.nmiss += set_range_to_miss(field.size, field.vec_d, field.missval, rmin, rmax);
+    field.numMissVals += set_range_to_miss(field.size, field.vec_d, field.missval, rmin, rmax);
 }
 
 template <typename T>
@@ -132,22 +132,38 @@ set_valid_range(size_t gridsize, Varray<T> &array, T missval, T rmin, T rmax)
   for (size_t i = 0; i < gridsize; ++i)
     if (array[i] < rmin || array[i] > rmax) array[i] = missval;
 
-  const auto nmiss = varray_num_mv(gridsize, array, missval);
+  const auto numMissVals = varray_num_mv(gridsize, array, missval);
 
-  return nmiss;
+  return numMissVals;
 }
 
 static void
 set_valid_range(Field &field, double rmin, double rmax)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = set_valid_range(field.size, field.vec_f, (float) field.missval, (float) rmin, (float) rmax);
+    field.numMissVals = set_valid_range(field.size, field.vec_f, (float) field.missval, (float) rmin, (float) rmax);
   else
-    field.nmiss = set_valid_range(field.size, field.vec_d, field.missval, rmin, rmax);
+    field.numMissVals = set_valid_range(field.size, field.vec_d, field.missval, rmin, rmax);
 }
 
-class ModuleSetmiss
+class Setmiss : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setmiss",
+    .operators = { { "setmissval", 0, 0, "missing value", SetmissHelp },
+                   { "setctomiss", 0, 0, "constant", SetmissHelp },
+                   { "setmisstoc", 0, 0, "constant", SetmissHelp },
+                   { "setrtomiss", 0, 0, "range(min,max)", SetmissHelp },
+                   { "setvrange", 0, 0, "range(min,max)", SetmissHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Setmiss> registration = RegisterEntry<Setmiss>(module);
+  int SETMISSVAL, SETCTOMISS, SETMISSTOC, SETRTOMISS, SETVRANGE;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -158,8 +174,6 @@ class ModuleSetmiss
   Field field;
   int operatorID;
 
-  int SETMISSVAL, SETCTOMISS, SETMISSTOC, SETRTOMISS, SETVRANGE;
-
   double rconst = 0.0;
   double rmin = 0.0;
   double rmax = 0.0;
@@ -167,16 +181,15 @@ class ModuleSetmiss
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-   SETMISSVAL = cdo_operator_add("setmissval", 0, 0, "missing value");
-   SETCTOMISS = cdo_operator_add("setctomiss", 0, 0, "constant");
-   SETMISSTOC = cdo_operator_add("setmisstoc", 0, 0, "constant");
-   SETRTOMISS = cdo_operator_add("setrtomiss", 0, 0, "range (min, max)");
-   SETVRANGE  = cdo_operator_add("setvrange",  0, 0, "range (min, max)");
+SETMISSVAL = module.get_id("setmissval");
+SETCTOMISS = module.get_id("setctomiss");
+SETMISSTOC = module.get_id("setmisstoc");
+SETRTOMISS = module.get_id("setrtomiss");
+SETVRANGE = module.get_id("setvrange");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -280,17 +293,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Setmiss(void *process)
-{
-  ModuleSetmiss setmiss;
-  setmiss.init(process);
-  setmiss.run();
-  setmiss.close();
-
-  return nullptr;
-}
diff --git a/src/Setpartab.cc b/src/Setpartab.cc
index 34725167b71f4c36fd2fe5942e06ecab79eda18d..bf7420feb4b36dc759cb8b0c6c902954a6022e4e 100644
--- a/src/Setpartab.cc
+++ b/src/Setpartab.cc
@@ -151,8 +151,24 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
     }
 }
 
-class ModuleSetpartab
+class Setpartab : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setpartab",
+    .operators = { { "setcodetab", 0, 0, "parameter code table name", SetHelp },
+                   { "setpartabc", 0, 0, "parameter table name", SetpartabHelp },
+                   { "setpartabp", 0, 0, "parameter table name", SetpartabHelp },
+                   { "setpartabn", 0, 0, "parameter table name", SetpartabHelp } },
+    .aliases = { { "setpartab", "setcodetab" }, { "setpartabv", "setpartabn" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Setpartab> registration = RegisterEntry<Setpartab>(module);
+
+  int SETCODETAB, SETPARTABC, SETPARTABP, SETPARTABN;
   int tableID = -1;
   int tableformat = 0;
   bool deleteVars = false;
@@ -172,19 +188,14 @@ class ModuleSetpartab
   Varray<double> array;
   VarList varList2;
 
-  int SETCODETAB, SETPARTABC, SETPARTABP, SETPARTABN;
-
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    SETCODETAB = cdo_operator_add("setcodetab", 0, 0, "parameter code table name");
-    SETPARTABC = cdo_operator_add("setpartabc", 0, 0, "parameter table name");
-    SETPARTABP = cdo_operator_add("setpartabp", 0, 0, "parameter table name");
-    SETPARTABN = cdo_operator_add("setpartabn", 0, 0, "parameter table name");
+    SETCODETAB = module.get_id("setcodetab");
+    SETPARTABC = module.get_id("setpartabc");
+    SETPARTABP = module.get_id("setpartabp");
+    SETPARTABN = module.get_id("setpartabn");
 
     auto operatorID = cdo_operator_id();
 
@@ -389,13 +400,13 @@ public:
 
             cdo_def_record(streamID2, varID2, levelID2);
 
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             auto missval = varList2[varID2].missval;
             auto gridsize = varList2[varID2].nwpv * varList2[varID2].gridsize;
 
-            if (nmiss && var.changemissval)
+            if (numMissVals && var.changemissval)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
@@ -432,7 +443,7 @@ public:
               }
 #endif
 
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_write_record(streamID2, array.data(), numMissVals);
 
             cmor_check_prep(var, gridsize, missval, array.data());
           }
@@ -455,17 +466,5 @@ public:
 
     cdo_convert_destroy();
 #endif
-
-    cdo_finish();
   }
 };
-void *
-Setpartab(void *process)
-{
-  ModuleSetpartab setpartab;
-  setpartab.init(process);
-  setpartab.run();
-  setpartab.close();
-
-  return nullptr;
-}
diff --git a/src/Setrcaname.cc b/src/Setrcaname.cc
index e88ed182aaf04f3451d32a49a03f00e525ca9ca5..3f3ad613ef5b7eade9c7af74bf617449050bb140 100644
--- a/src/Setrcaname.cc
+++ b/src/Setrcaname.cc
@@ -5,22 +5,26 @@
 
 */
 
+#include <fstream>
+
 #include <cdi.h>
 
 #include "process_int.h"
-#include "readline.h"
 #include "cdo_zaxis.h"
 
-#define MAX_LINE_LEN 4096
-
-class ModuleSetrcaname
+class Setrcaname : public Process
 {
-  const char *rcsnames;
-  char line[MAX_LINE_LEN];
-  char sname[CDI_MAX_NAME], sdescription[CDI_MAX_NAME], sunits[CDI_MAX_NAME];
-  int scode, sltype, slevel;
-  int zaxisID, ltype, code, nlev;
-  int level;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setrcaname",
+    .operators = { { "setrcaname"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static auto registration = RegisterEntry<Setrcaname>(module);
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -39,47 +43,42 @@ class ModuleSetrcaname
   Field field;
 
   void
-  read_rca(const char *p_filename, int p_nvars, int p_vlistID2)
+  read_rca(const std::string &filename, int p_nvars, int p_vlistID2)
   {
-    auto fp = std::fopen(p_filename, "r");
-    if (fp != nullptr)
+    std::ifstream file(filename);
+    if (!file.is_open()) cdo_abort("Open failed on: %s\n", filename);
+
+    std::string line;
+    while (std::getline(file, line))
       {
-        while (cdo::readline(fp, line, MAX_LINE_LEN))
+        char sname[CDI_MAX_NAME], sdescription[CDI_MAX_NAME], sunits[CDI_MAX_NAME];
+        int scode, sltype, slevel;
+        std::sscanf(line.c_str(), "%d\t%d\t%d\t%s\t%s\t%s", &scode, &sltype, &slevel, sname, sdescription, sunits);
+        /*
+        printf("%s\n", line);
+        printf("%d:%d:%d:%s:%s:%s\n", scode, sltype, slevel, sname,
+        sdescription, sunits);
+        */
+        for (int varID = 0; varID < p_nvars; ++varID)
           {
-            std::sscanf(line, "%d\t%d\t%d\t%s\t%s\t%s", &scode, &sltype, &slevel, sname, sdescription, sunits);
-            /*
-            printf("%s\n", line);
-            printf("%d:%d:%d:%s:%s:%s\n", scode, sltype, slevel, sname,
-            sdescription, sunits);
-            */
-            for (int varID = 0; varID < p_nvars; ++varID)
-              {
-                code = vlistInqVarCode(p_vlistID2, varID);
-                zaxisID = vlistInqVarZaxis(p_vlistID2, varID);
-                nlev = zaxisInqSize(zaxisID);
+            auto code = vlistInqVarCode(p_vlistID2, varID);
+            auto zaxisID = vlistInqVarZaxis(p_vlistID2, varID);
+            auto nlev = zaxisInqSize(zaxisID);
 
-                ltype = zaxis_to_ltype(zaxisID);
+            auto ltype = zaxis_to_ltype(zaxisID);
 
-                if (code == scode)
+            if (code == scode)
+              {
+                if (ltype == 105)
                   {
-                    if (ltype == 105)
+                    if (nlev != 1)
                       {
-                        if (nlev != 1)
-                          {
-                            cdo_warning("Number of levels should be 1 for level type 105!");
-                            cdo_warning("Maybe environment variable SPLIT_LTYPE_105 is not set.");
-                            continue;
-                          }
-                        level = (int) cdo_zaxis_inq_level(zaxisID, 0);
-                        if (sltype == 105 && slevel == level)
-                          {
-                            cdiDefKeyString(p_vlistID2, varID, CDI_KEY_NAME, sname);
-                            cdiDefKeyString(p_vlistID2, varID, CDI_KEY_LONGNAME, sdescription);
-                            cdiDefKeyString(p_vlistID2, varID, CDI_KEY_UNITS, sunits);
-                            break;
-                          }
+                        cdo_warning("Number of levels should be 1 for level type 105!");
+                        cdo_warning("Maybe environment variable SPLIT_LTYPE_105 is not set.");
+                        continue;
                       }
-                    else if (sltype != 105)
+                    auto level = (int) cdo_zaxis_inq_level(zaxisID, 0);
+                    if (sltype == 105 && slevel == level)
                       {
                         cdiDefKeyString(p_vlistID2, varID, CDI_KEY_NAME, sname);
                         cdiDefKeyString(p_vlistID2, varID, CDI_KEY_LONGNAME, sdescription);
@@ -87,24 +86,28 @@ class ModuleSetrcaname
                         break;
                       }
                   }
+                else if (sltype != 105)
+                  {
+                    cdiDefKeyString(p_vlistID2, varID, CDI_KEY_NAME, sname);
+                    cdiDefKeyString(p_vlistID2, varID, CDI_KEY_LONGNAME, sdescription);
+                    cdiDefKeyString(p_vlistID2, varID, CDI_KEY_UNITS, sunits);
+                    break;
+                  }
               }
           }
-
-        std::fclose(fp);
       }
-    else { perror(rcsnames); }
+
+    file.close();
   }
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
     operator_input_arg("file name with RCA names");
-    rcsnames = cdo_operator_argv(0).c_str();
+    auto rcsnames = cdo_operator_argv(0);
 
     streamID1 = cdo_open_read(0);
 
@@ -164,17 +167,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Setrcaname(void *process)
-{
-  ModuleSetrcaname setrcaname;
-  setrcaname.init(process);
-  setrcaname.run();
-  setrcaname.close();
-  return nullptr;
-}
diff --git a/src/Settime.cc b/src/Settime.cc
index 6690f624ecccbefdc5896a924ab08f7609f49304..3ce9d159f784964272ed375cce83df82532c443f 100644
--- a/src/Settime.cc
+++ b/src/Settime.cc
@@ -162,8 +162,30 @@ timeunits_is_valid(int timeUnits)
           || timeUnits == TUNIT_DAY || timeUnits == TUNIT_MONTH || timeUnits == TUNIT_YEAR);
 }
 
-class ModuleSettime
+class Settime : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Settime",
+    .operators = { { "setyear", 0, 1, "year", SettimeHelp },
+                   { "setmon", 0, 1, "month", SettimeHelp },
+                   { "setday", 0, 1, "day", SettimeHelp },
+                   { "setdate", 0, 1, "date(format:YYYY-MM-DD)", SettimeHelp },
+                   { "settime", 0, 1, "time(format:hh:mm:ss)", SettimeHelp },
+                   { "settunits", 0, 1, "timeunits(seconds,minutes,hours,days,months,years)", SettimeHelp },
+                   { "settaxis", 0, -2, "date<,time<,increment>>(formatYYYY-MM-DD,hh:mm:ss)", SettimeHelp },
+                   { "settbounds", 0, 1, "frequency(hour,day,month,year)", SettimeHelp },
+                   { "setreftime", 0, -2, "date<,time<,units>>(formatYYYY-MM-DD,hh:mm:ss)", SettimeHelp },
+                   { "setcalendar", 0, 1, "calendar(standard,proleptic_gregorian,360_day,365_day,366_day)", SettimeHelp },
+                   { "shifttime", 0, 1, "shiftvalue", SettimeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Settime> registration = RegisterEntry<Settime>(module);
+  int SETYEAR, SETMON, SETDAY, SETDATE, SETTIME, SETTUNITS, SETTAXIS, SETTBOUNDS, SETREFTIME, SETCALENDAR, SHIFTTIME;
   int64_t newval = 0;
   int timeUnits = TUNIT_DAY;
   int64_t ijulinc = 0;
@@ -178,8 +200,6 @@ class ModuleSettime
   CdiDateTime vDateTimeBounds[2]{};
   JulianDate julianDate;
 
-  int SETYEAR, SETMON, SETDAY, SETDATE, SETTIME, SETTUNITS, SETTAXIS, SETTBOUNDS, SETREFTIME, SETCALENDAR, SHIFTTIME;
-
   CdoStreamID streamID1;
   CdoStreamID streamID2 = CDO_STREAM_UNDEF;
 
@@ -198,23 +218,21 @@ class ModuleSettime
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     // clang-format off
-   SETYEAR     = cdo_operator_add("setyear",      0,  1, "year");
-   SETMON      = cdo_operator_add("setmon",       0,  1, "month");
-   SETDAY      = cdo_operator_add("setday",       0,  1, "day");
-   SETDATE     = cdo_operator_add("setdate",      0,  1, "date (format: YYYY-MM-DD)");
-   SETTIME     = cdo_operator_add("settime",      0,  1, "time (format: hh:mm:ss)");
-   SETTUNITS   = cdo_operator_add("settunits",    0,  1, "time units (seconds, minutes, hours, days, months, years)");
-   SETTAXIS    = cdo_operator_add("settaxis",     0, -2, "date<,time<,increment>> (format YYYY-MM-DD,hh:mm:ss)");
-   SETTBOUNDS  = cdo_operator_add("settbounds",   0,  1, "frequency (hour, day, month, year)");
-   SETREFTIME  = cdo_operator_add("setreftime",   0, -2, "date<,time<,units>> (format YYYY-MM-DD,hh:mm:ss)");
-   SETCALENDAR = cdo_operator_add("setcalendar",  0,  1, "calendar (standard, proleptic_gregorian, 360_day, 365_day, 366_day)");
-   SHIFTTIME   = cdo_operator_add("shifttime",    0,  1, "shift value");
+SETYEAR = module.get_id("setyear");
+SETMON = module.get_id("setmon");
+SETDAY = module.get_id("setday");
+SETDATE = module.get_id("setdate");
+SETTIME = module.get_id("settime");
+SETTUNITS = module.get_id("settunits");
+SETTAXIS = module.get_id("settaxis");
+SETTBOUNDS = module.get_id("settbounds");
+SETREFTIME = module.get_id("setreftime");
+SETCALENDAR = module.get_id("setcalendar");
+SHIFTTIME = module.get_id("shifttime");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -477,18 +495,5 @@ public:
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Settime(void *process)
-{
-
-  ModuleSettime settime;
-  settime.init(process);
-  settime.run();
-  settime.close();
-  return nullptr;
-}
diff --git a/src/Setzaxis.cc b/src/Setzaxis.cc
index 9f77b17b8dd20cb3655ab0bae6f9a1149d867f6f..76241a1b7d109ba416a30b55dfaa28c0b29ca52d 100644
--- a/src/Setzaxis.cc
+++ b/src/Setzaxis.cc
@@ -39,8 +39,21 @@ getkeyval_dp(const std::string &p_keyval, const char *p_key, double *val)
   return status;
 }
 
-class ModuleSetzaxis
+class Setzaxis : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Setzaxis",
+    .operators = { { "setzaxis", 0, 0, "zaxis description file", SetzaxisHelp }, { "genlevelbounds", SetzaxisHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Setzaxis> registration = RegisterEntry<Setzaxis>(module);
+
+  int SETZAXIS, GENLEVELBOUNDS;
 
   int zaxisID1, zaxisID2 = -1;
   int nzaxis, index;
@@ -58,19 +71,12 @@ class ModuleSetzaxis
 
   int operatorID;
 
-  int SETZAXIS, GENLEVELBOUNDS;
-
 public:
   void
-  init(void *process)
+  init()
   {
-
-    cdo_initialize(process);
-
-    // clang-format off
-   SETZAXIS       = cdo_operator_add("setzaxis",        0, 0, "zaxis description file");
-   GENLEVELBOUNDS = cdo_operator_add("genlevelbounds",  0, 0, nullptr);
-    // clang-format on
+    SETZAXIS = module.get_id("setzaxis");
+    GENLEVELBOUNDS = module.get_id("genlevelbounds");
 
     operatorID = cdo_operator_id();
 
@@ -192,9 +198,9 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
-            cdo_write_record(streamID2, array.data(), nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
+            cdo_write_record(streamID2, array.data(), numMissVals);
           }
 
         tsID++;
@@ -206,16 +212,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-    cdo_finish();
   }
 };
-
-void *
-Setzaxis(void *process)
-{
-  ModuleSetzaxis setzaxis;
-  setzaxis.init(process);
-  setzaxis.run();
-  setzaxis.close();
-  return nullptr;
-}
diff --git a/src/Shiftxy.cc b/src/Shiftxy.cc
index 79bf5693244179db709383ee622891913bae07d9..ba8c87516da30e6d24568229c7cfc495b612b46e 100644
--- a/src/Shiftxy.cc
+++ b/src/Shiftxy.cc
@@ -80,9 +80,9 @@ shifty(bool fillCyclic, int numberOfShifts, int nx, int ny, Varray<double> &v1,
 static int
 shiftx_coord(bool fillCyclic, int numberOfShifts, int gridID1)
 {
-  const auto gridID2 = gridDuplicate(gridID1);
+  auto gridID2 = gridDuplicate(gridID1);
 
-  const auto nx = gridInqXsize(gridID1);
+  auto nx = gridInqXsize(gridID1);
   auto ny = gridInqYsize(gridID1);
   if (gridInqType(gridID1) != GRID_CURVILINEAR) ny = 1;
 
@@ -93,7 +93,7 @@ shiftx_coord(bool fillCyclic, int numberOfShifts, int gridID1)
 
   if (gridInqXbounds(gridID1, nullptr))
     {
-      const size_t nv = (gridInqType(gridID1) != GRID_CURVILINEAR) ? 2 : 4;
+      size_t nv = (gridInqType(gridID1) != GRID_CURVILINEAR) ? 2 : 4;
 
       Varray<double> bounds(nx * ny * nv);
       gridInqXbounds(gridID1, bounds.data());
@@ -112,10 +112,10 @@ shiftx_coord(bool fillCyclic, int numberOfShifts, int gridID1)
 static int
 shifty_coord(bool fillCyclic, int numberOfShifts, int gridID1)
 {
-  const auto gridID2 = gridDuplicate(gridID1);
+  auto gridID2 = gridDuplicate(gridID1);
 
   auto nx = gridInqXsize(gridID1);
-  const auto ny = gridInqYsize(gridID1);
+  auto ny = gridInqYsize(gridID1);
   if (gridInqType(gridID1) != GRID_CURVILINEAR) nx = 1;
 
   Varray<double> v1(nx * ny), v2(nx * ny);
@@ -125,7 +125,7 @@ shifty_coord(bool fillCyclic, int numberOfShifts, int gridID1)
 
   if (gridInqYbounds(gridID1, nullptr))
     {
-      const size_t nv = (gridInqType(gridID1) != GRID_CURVILINEAR) ? 2 : 4;
+      size_t nv = (gridInqType(gridID1) != GRID_CURVILINEAR) ? 2 : 4;
 
       Varray<double> bounds(nx * ny * nv);
       gridInqYbounds(gridID1, bounds.data());
@@ -141,10 +141,22 @@ shifty_coord(bool fillCyclic, int numberOfShifts, int gridID1)
   return gridID2;
 }
 
-class ModuleShiftxy
+class Shiftxy : public Process
 {
-  int operatorID;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Shiftxy",
+    .operators = { { "shiftx", ShiftxyHelp }, { "shifty", ShiftxyHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Shiftxy> registration = RegisterEntry<Shiftxy>(module);
+
   int SHIFTX, SHIFTY;
+  int operatorID;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -166,12 +178,10 @@ class ModuleShiftxy
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    SHIFTX = cdo_operator_add("shiftx", 0, 0, nullptr);
-    SHIFTY = cdo_operator_add("shifty", 0, 0, nullptr);
+    SHIFTX = module.get_id("shiftx");
+    SHIFTY = module.get_id("shifty");
 
     operatorID = cdo_operator_id();
 
@@ -198,14 +208,14 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    const auto nvars = vlistNvars(vlistID1);
+    auto nvars = vlistNvars(vlistID1);
     vars = std::vector<bool>(nvars, false);
 
-    const auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNgrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
-        const auto gridID1 = vlistGrid(vlistID1, index);
-        const auto gridtype = gridInqType(gridID1);
+        auto gridID1 = vlistGrid(vlistID1, index);
+        auto gridtype = gridInqType(gridID1);
 
         if (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_CURVILINEAR
             || (gridtype == GRID_PROJECTION && gridInqProjType(gridID1) == CDI_PROJ_RLL)
@@ -239,7 +249,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    const auto gridsizemax = vlistGridsizeMax(vlistID1);
+    auto gridsizemax = vlistGridsizeMax(vlistID1);
     array1 = Varray<double>(gridsizemax);
     array2 = Varray<double>(gridsizemax);
   }
@@ -250,7 +260,7 @@ public:
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -260,29 +270,29 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t nmiss;
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             cdo_def_record(streamID2, varID, levelID);
 
             if (vars[varID])
               {
-                const auto gridID1 = vlistInqVarGrid(vlistID1, varID);
-                const auto gridsize = gridInqSize(gridID1);
-                const auto missval = vlistInqVarMissval(vlistID2, varID);
+                auto gridID1 = vlistInqVarGrid(vlistID1, varID);
+                auto gridsize = gridInqSize(gridID1);
+                auto missval = vlistInqVarMissval(vlistID2, varID);
 
-                const auto nx = gridInqXsize(gridID1);
-                const auto ny = gridInqYsize(gridID1);
+                auto nx = gridInqXsize(gridID1);
+                auto ny = gridInqYsize(gridID1);
 
                 if (operatorID == SHIFTX)
                   shiftx(fillCyclic, numberOfShifts, nx, ny, array1, array2, missval);
                 else if (operatorID == SHIFTY)
                   shifty(fillCyclic, numberOfShifts, nx, ny, array1, array2, missval);
 
-                nmiss = varray_num_mv(gridsize, array2, missval);
-                cdo_write_record(streamID2, array2.data(), nmiss);
+                numMissVals = varray_num_mv(gridsize, array2, missval);
+                cdo_write_record(streamID2, array2.data(), numMissVals);
               }
-            else { cdo_write_record(streamID2, array1.data(), nmiss); }
+            else { cdo_write_record(streamID2, array1.data(), numMissVals); }
           }
         tsID++;
       }
@@ -295,17 +305,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Shiftxy(void *process)
-{
-  ModuleShiftxy shiftxy;
-  shiftxy.init(process);
-  shiftxy.run();
-  shiftxy.close();
-  return nullptr;
-}
diff --git a/src/Showattribute.cc b/src/Showattribute.cc
index 680ae9383db27bcff58a4dd9be5d92c97a9218f0..095ef685878e48ad6afaf83a49f6ab27d22de06b 100644
--- a/src/Showattribute.cc
+++ b/src/Showattribute.cc
@@ -11,6 +11,7 @@
 #include "process_int.h"
 #include "util_wildcards.h"
 #include "util_string.h"
+#include "cdi_uuid.h"
 
 void
 print_attributes(const VarList &varList, int vlistID, int varOrGlobal, int natts, char *argument)
@@ -107,6 +108,50 @@ print_attributes(const VarList &varList, int vlistID, int varOrGlobal, int natts
         }
       else { cdo_warning("Unsupported type %i name %s", atttype, attname); }
     }
+
+  if (varOrGlobal == CDI_GLOBAL)
+    {
+      auto gridID = vlistInqVarGrid(vlistID, 0);
+      if (gridInqType(gridID) == GRID_UNSTRUCTURED)
+        {
+          {
+            const char *attname = "number_of_grid_used";
+            if (argument == nullptr || (argument && wildcardmatch(argument, attname) == 0))
+              {
+                int number = 0;
+                cdiInqKeyInt(gridID, CDI_GLOBAL, CDI_KEY_NUMBEROFGRIDUSED, &number);
+                if (number > 0) fprintf(stdout, "   %s = %d\n", attname, number);
+              }
+          }
+          {
+            const char *attname = "grid_file_uri";
+            if (argument == nullptr || (argument && wildcardmatch(argument, attname) == 0))
+              {
+                int length = 0;
+                if (CDI_NOERR == cdiInqKeyLen(gridID, CDI_GLOBAL, CDI_KEY_REFERENCEURI, &length))
+                  {
+                    char referenceLink[8192];
+                    cdiInqKeyString(gridID, CDI_GLOBAL, CDI_KEY_REFERENCEURI, referenceLink, &length);
+                    fprintf(stdout, "   %s = \"%s\"\n", attname, referenceLink);
+                  }
+              }
+          }
+          {
+            const char *attname = "uuidOfHGrid";
+            if (argument == nullptr || (argument && wildcardmatch(argument, attname) == 0))
+              {
+                unsigned char uuid[CDI_UUID_SIZE] = { 0 };
+                int length = CDI_UUID_SIZE;
+                auto status = cdiInqKeyBytes(gridID, CDI_GLOBAL, CDI_KEY_UUID, uuid, &length);
+                if (status == CDI_NOERR && !cdiUUIDIsNull(uuid))
+                  {
+                    char uuidStr[uuidNumHexChars + 1] = { 0 };
+                    if (cdiUUID2Str(uuid, uuidStr) == uuidNumHexChars) fprintf(stdout, "   %s = \"%s\"\n", attname, uuidStr);
+                  }
+              }
+          }
+        }
+    }
 }
 
 void
@@ -129,9 +174,23 @@ check_varname_and_print(const VarList &varList, int vlistID, int nvars, char *ch
   if (!lfound && checkvarname) cdo_abort("Could not find variable %s!", checkvarname);
 }
 
-class ModuleShowattribute
+class Showattribute : public Process
 {
-  int delim = '@';
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Showattribute",
+    // clang-format off
+    .operators = { { "showattribute", ShowattributeHelp },
+                   { "showattsvar", ShowattributeHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Showattribute> registration = RegisterEntry<Showattribute>(module);
+
   int SHOWATTRIBUTE, SHOWATTSVAR;
 
   CdoStreamID streamID;
@@ -143,12 +202,10 @@ class ModuleShowattribute
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    SHOWATTRIBUTE = cdo_operator_add("showattribute", 0, 0, nullptr);
-    SHOWATTSVAR = cdo_operator_add("showattsvar", 0, 0, nullptr);
+    SHOWATTRIBUTE = module.get_id("showattribute");
+    SHOWATTSVAR = module.get_id("showattsvar");
 
     operatorID = cdo_operator_id();
 
@@ -188,6 +245,7 @@ public:
       }
     else
       {
+        constexpr int delim = '@';
         auto params = cdo_get_oper_argv();
         char buffer[CDI_MAX_NAME];
         for (int i = 0; i < nargs; ++i)
@@ -224,21 +282,10 @@ public:
           }
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID);
-
-    cdo_finish();
   }
 };
-
-void *
-Showattribute(void *process)
-{
-  ModuleShowattribute showattribute;
-  showattribute.init(process);
-  showattribute.run();
-  showattribute.close();
-  return nullptr;
-}
diff --git a/src/Showinfo.cc b/src/Showinfo.cc
index 11735c70b4b001cc1789184faff3c01b9d523464..6ee2f28c852fe31d790e0a9127e4f6c3cd520b09 100644
--- a/src/Showinfo.cc
+++ b/src/Showinfo.cc
@@ -341,8 +341,34 @@ show_atts(int vlistID, const VarList &varList)
       print_attributes(varList, vlistID, varID, nattsvar, nullptr);
     }
 }
-class ModuleShowinfo
+class Showinfo : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Showinfo",
+    .operators = { { "showyear", ShowinfoHelp },
+                   { "showmon", ShowinfoHelp },
+                   { "showdate", ShowinfoHelp },
+                   { "showtime", ShowinfoHelp },
+                   { "showtimestamp", ShowinfoHelp },
+                   { "showcode", ShowinfoHelp },
+                   { "showunit", ShowinfoHelp },
+                   { "showparam", ShowinfoHelp },
+                   { "showname", ShowinfoHelp },
+                   { "showstdname", ShowinfoHelp },
+                   { "showlevel", ShowinfoHelp },
+                   { "showltype", ShowinfoHelp },
+                   { "showformat", ShowinfoHelp },
+                   { "showgrid", ShowinfoHelp },
+                   { "showatts", ShowinfoHelp },
+                   { "showattsglob", ShowinfoHelp } },
+    .aliases = { { "showvar", "showname" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Showinfo> registration = RegisterEntry<Showinfo>(module);
   int SHOWYEAR, SHOWMON, SHOWDATE, SHOWTIME, SHOWTIMESTAMP, SHOWCODE, SHOWUNIT, SHOWPARAM, SHOWNAME, SHOWSTDNAME, SHOWLEVEL,
       SHOWLTYPE, SHOWFORMAT, SHOWGRID, SHOWATTS, SHOWATTSGLOB;
   int operatorID;
@@ -352,27 +378,26 @@ class ModuleShowinfo
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  SHOWYEAR      = cdo_operator_add("showyear",      0, 0, nullptr);
-  SHOWMON       = cdo_operator_add("showmon",       0, 0, nullptr);
-  SHOWDATE      = cdo_operator_add("showdate",      0, 0, nullptr);
-  SHOWTIME      = cdo_operator_add("showtime",      0, 0, nullptr);
-  SHOWTIMESTAMP = cdo_operator_add("showtimestamp", 0, 0, nullptr);
-  SHOWCODE      = cdo_operator_add("showcode",      0, 0, nullptr);
-  SHOWUNIT      = cdo_operator_add("showunit",      0, 0, nullptr);
-  SHOWPARAM     = cdo_operator_add("showparam",     0, 0, nullptr);
-  SHOWNAME      = cdo_operator_add("showname",      0, 0, nullptr);
-  SHOWSTDNAME   = cdo_operator_add("showstdname",   0, 0, nullptr);
-  SHOWLEVEL     = cdo_operator_add("showlevel",     0, 0, nullptr);
-  SHOWLTYPE     = cdo_operator_add("showltype",     0, 0, nullptr);
-  SHOWFORMAT    = cdo_operator_add("showformat",    0, 0, nullptr);
-  SHOWGRID      = cdo_operator_add("showgrid",      0, 0, nullptr);
-  SHOWATTS      = cdo_operator_add("showatts",      0, 0, nullptr);
-  SHOWATTSGLOB  = cdo_operator_add("showattsglob",  0, 0, nullptr);
+SHOWYEAR = module.get_id("showyear");
+SHOWMON = module.get_id("showmon");
+SHOWDATE = module.get_id("showdate");
+SHOWTIME = module.get_id("showtime");
+SHOWTIMESTAMP = module.get_id("showtimestamp");
+SHOWCODE = module.get_id("showcode");
+SHOWUNIT = module.get_id("showunit");
+SHOWPARAM = module.get_id("showparam");
+SHOWNAME = module.get_id("showname");
+SHOWSTDNAME = module.get_id("showstdname");
+SHOWLEVEL = module.get_id("showlevel");
+SHOWLTYPE = module.get_id("showltype");
+SHOWFORMAT = module.get_id("showformat");
+SHOWGRID = module.get_id("showgrid");
+SHOWATTS = module.get_id("showatts");
+SHOWATTSGLOB = module.get_id("showattsglob");
   operatorID = cdo_operator_id();
 
   operator_check_argc(0);
@@ -413,16 +438,5 @@ public:
   close()
   {
     cdo_stream_close(streamID);
-
-    cdo_finish();
   }
 };
-void *
-Showinfo(void *process)
-{
-  ModuleShowinfo showinfo;
-  showinfo.init(process);
-  showinfo.run();
-  showinfo.close();
-  return nullptr;
-}
diff --git a/src/Sinfo.cc b/src/Sinfo.cc
index 7a7ae9da3863281bf43174111e9eed389e99f791..3906a1d7f380615a4d6d6ee20e57516d02ccbda7 100644
--- a/src/Sinfo.cc
+++ b/src/Sinfo.cc
@@ -86,10 +86,7 @@ get_num_output_bits(int datatype)
     {
       if (CdoDefault::FileType != CDI_UNDEFID) {}
     }
-  else
-    {
-      datatype = CdoDefault::DataType;
-    }
+  else { datatype = CdoDefault::DataType; }
 
   return get_num_input_bits(datatype);
 }
@@ -307,16 +304,10 @@ print_time_info_xs(int ntsteps, int taxisID, CdoStreamID streamID)
               auto jdelta = julianDate_to_seconds(julianDate_sub(julianDate, julianDate0));
               timeIncrement = get_time_increment(jdelta, vDateTimeLast.date, vDateTime.date);
             }
-          else
-            {
-              vDateTimeFirst = vDateTime;
-            }
+          else { vDateTimeFirst = vDateTime; }
 
           if (tsID == 1) { timeIncrement0 = timeIncrement; }
-          else if (tsID > 1 && timeIncrement0 != timeIncrement)
-            {
-              timeIncrement0.period = 0;
-            }
+          else if (tsID > 1 && timeIncrement0 != timeIncrement) { timeIncrement0.period = 0; }
 
           vDateTimeLast = vDateTime;
 
@@ -379,27 +370,31 @@ print_time_info_xs(int ntsteps, int taxisID, CdoStreamID streamID)
   return numTimesteps;
 }
 
-static void
-add_operators(void)
+class Sinfo : public Process
 {
-  // clang-format off
-  cdo_operator_add("sinfo",   func_generic, 0, nullptr);
-  cdo_operator_add("sinfop",  func_param,   0, nullptr);
-  cdo_operator_add("sinfon",  func_name,    0, nullptr);
-  cdo_operator_add("sinfoc",  func_code,    0, nullptr);
-  cdo_operator_add("seinfo",  func_generic, 1, nullptr);
-  cdo_operator_add("seinfop", func_param,   1, nullptr);
-  cdo_operator_add("seinfon", func_name,    1, nullptr);
-  cdo_operator_add("seinfoc", func_code,    1, nullptr);
-  cdo_operator_add("xsinfo",  func_name,    2, nullptr);
-  cdo_operator_add("xsinfop", func_param,   2, nullptr);
-  cdo_operator_add("xsinfon", func_name,    2, nullptr);
-  cdo_operator_add("xsinfoc", func_code,    2, nullptr);
-  // clang-format on
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Sinfo",
+    .operators = { { "sinfo", func_generic, 0, SinfoHelp },
+                   { "sinfop", func_param, 0, SinfoHelp },
+                   { "sinfon", func_name, 0, SinfoHelp },
+                   { "sinfoc", func_code, 0, SinfoHelp },
+                   { "seinfo", func_generic, 1, SinfoHelp },
+                   { "seinfop", func_param, 1, SinfoHelp },
+                   { "seinfon", func_name, 1, SinfoHelp },
+                   { "seinfoc", func_code, 1, SinfoHelp },
+                   { "xsinfo", func_name, 2, XSinfoHelp },
+                   { "xsinfop", func_param, 2, XSinfoHelp },
+                   { "xsinfon", func_name, 2, XSinfoHelp },
+                   { "xsinfoc", func_code, 2, XSinfoHelp } },
+    .aliases = { { "infov", "infon" }, { "sinfov", "sinfon" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { -1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Sinfo> registration = RegisterEntry<Sinfo>(module);
 
-class ModuleSinfo
-{
 private:
   int operfunc;
   int ensembleInfo;
@@ -407,11 +402,8 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
 
@@ -553,16 +545,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Sinfo(void *process)
-{
-  ModuleSinfo sinfo;
-  sinfo.init(process);
-  sinfo.run();
-  sinfo.close();
-  return nullptr;
-}
diff --git a/src/Smooth.cc b/src/Smooth.cc
index 30c8f23ac8c03b02594d4b72614ba202f5debee2..d7550a961d9654008a2c2a664cb72480fb9b5e98 100644
--- a/src/Smooth.cc
+++ b/src/Smooth.cc
@@ -117,7 +117,7 @@ smooth(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2, const
 
   progress::update(0, 1, 1);
 
-  size_t nmissx = atomicNumMiss;
+  size_t numMissValsx = atomicNumMiss;
   size_t numPoints = atomicSum;
 
   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds (%zu points)", timer.elapsed(), numPoints);
@@ -127,7 +127,7 @@ smooth(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2, const
 
   if (gridID0 != gridID) gridDestroy(gridID);
 
-  return nmissx;
+  return numMissValsx;
 }
 
 static void
@@ -136,9 +136,9 @@ smooth(const Field &field1, Field &field2, const SmoothPoint &spoint)
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    field2.nmiss = smooth(field1.grid, (float) field1.missval, field1.vec_f, field2.vec_f, spoint);
+    field2.numMissVals = smooth(field1.grid, (float) field1.missval, field1.vec_f, field2.vec_f, spoint);
   else
-    field2.nmiss = smooth(field1.grid, field1.missval, field1.vec_d, field2.vec_d, spoint);
+    field2.numMissVals = smooth(field1.grid, field1.missval, field1.vec_d, field2.vec_d, spoint);
 }
 
 template <typename T>
@@ -165,7 +165,7 @@ smooth9(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2)
 
   for (size_t i = 0; i < gridsize; ++i) mask[i] = !dbl_is_equal(missval, array1[i]);
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < nlat; ++i)
     {
       for (size_t j = 0; j < nlon; ++j)
@@ -241,12 +241,12 @@ smooth9(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2)
           else
             {
               array2[i * nlon + j] = missval;
-              nmiss++;
+              numMissVals++;
             }
         }
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 static void
@@ -255,15 +255,15 @@ smooth9(const Field &field1, Field &field2)
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    field2.nmiss = smooth9(field1.grid, (float) field1.missval, field1.vec_f, field2.vec_f);
+    field2.numMissVals = smooth9(field1.grid, (float) field1.missval, field1.vec_f, field2.vec_f);
   else
-    field2.nmiss = smooth9(field1.grid, field1.missval, field1.vec_d, field2.vec_d);
+    field2.numMissVals = smooth9(field1.grid, field1.missval, field1.vec_d, field2.vec_d);
 }
 
 double
 radiusDegToKm(double radiusInDeg)
 {
-  return radiusInDeg * (2.0 * PlanetRadius * M_PI) / (360.0 * 1000.0);
+  return radiusInDeg * (2.0 * PlanetRadiusDefault * M_PI) / (360.0 * 1000.0);
 }
 
 static CurveForm
@@ -289,7 +289,7 @@ get_parameter(int &xnsmooth, SmoothPoint &spoint)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -335,8 +335,21 @@ check_radius_range(double radius, const char *name)
   if (radius < 0.0 || radius > 180.0) cdo_abort("%s=%g out of bounds (0-180 deg)!", name, radius);
 }
 
-class ModuleSmooth
+class Smooth : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Smooth",
+    .operators = { { "smooth", SmoothHelp }, { "smooth9", SmoothHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Smooth> registration = RegisterEntry<Smooth>(module);
+
+  int SMOOTH, SMOOTH9;
   int nvars;
   VarList varList1;
   Field field1, field2;
@@ -355,18 +368,12 @@ class ModuleSmooth
 
   SmoothPoint spoint;
 
-  int SMOOTH, SMOOTH9;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    SMOOTH  = cdo_operator_add("smooth",   0,   0, nullptr);
-    SMOOTH9 = cdo_operator_add("smooth9",  0,   0, nullptr);
-    // clang-format on
+    SMOOTH = module.get_id("smooth");
+    SMOOTH9 = module.get_id("smooth9");
 
     operatorID = cdo_operator_id();
 
@@ -462,18 +469,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Smooth(void *process)
-{
-  ModuleSmooth smooth;
-  smooth.init(process);
-  smooth.run();
-  smooth.close();
-
-  return nullptr;
-}
diff --git a/src/Sort.cc b/src/Sort.cc
index 1cb377f297352361599851ee82eb21a1b66771aa..5c9085a342d68d1a67878b31baa5a2eb288e0254 100644
--- a/src/Sort.cc
+++ b/src/Sort.cc
@@ -11,8 +11,6 @@
       Sort sortcode  Sort by code number
 */
 
-#include <algorithm>  // sort
-
 #include <cdi.h>
 
 #include "cdo_options.h"
@@ -24,72 +22,58 @@
 struct LevInfo
 {
   int levelID;
-  size_t nmiss;
+  size_t numMissVals;
   double level;
 };
 
 struct VarInfo
 {
   int varID;
-  int nlevs;
+  int nlevels;
   int code;
-  char param[CDI_MAX_NAME];
-  char name[CDI_MAX_NAME];
+  std::string param;
+  std::string name;
   std::vector<LevInfo> levInfo;
 };
 
-static bool
-cmpvarcode(const VarInfo &a, const VarInfo &b)
-{
-  return a.code < b.code;
-}
-
-static bool
-cmpvarparam(const VarInfo &a, const VarInfo &b)
-{
-  return strcmp(a.param, b.param) < 0;
-}
-
-static bool
-cmpvarname(const VarInfo &a, const VarInfo &b)
-{
-  return strcmp(a.name, b.name) < 0;
-}
-
-static bool
-cmpvarlevel(const LevInfo &a, const LevInfo &b)
-{
-  return a.level < b.level;
-}
-
-static bool
-cmpvarlevelrev(const LevInfo &a, const LevInfo &b)
-{
-  return a.level > b.level;
-}
-
 static void
-setNmiss(int varID, int levelID, int nvars, std::vector<VarInfo> &varInfo, size_t nmiss)
+setNmiss(int varID, int levelID, int nvars, std::vector<VarInfo> &varsInfo, size_t numMissVals)
 {
   int vindex, lindex;
 
   for (vindex = 0; vindex < nvars; vindex++)
-    if (varInfo[vindex].varID == varID) break;
+    if (varsInfo[vindex].varID == varID) break;
 
   if (vindex == nvars) cdo_abort("Internal problem; varID not found!");
 
-  auto nlevels = varInfo[vindex].nlevs;
+  auto nlevels = varsInfo[vindex].nlevels;
   for (lindex = 0; lindex < nlevels; lindex++)
-    if (varInfo[vindex].levInfo[lindex].levelID == levelID) break;
+    if (varsInfo[vindex].levInfo[lindex].levelID == levelID) break;
 
   if (lindex == nlevels) cdo_abort("Internal problem; levelID not found!");
 
-  varInfo[vindex].levInfo[lindex].nmiss = nmiss;
+  varsInfo[vindex].levInfo[lindex].numMissVals = numMissVals;
 }
 
-class ModuleSort
+class Sort : public Process
 {
-  bool (*cmpvarlev)(const LevInfo &a, const LevInfo &b) = cmpvarlevel;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Sort",
+    .operators = { { "sortcode"},
+                   { "sortparam"},
+                   { "sortname"},
+                   { "sortlevel"} },
+    .aliases = { { "sortvar", "sortname" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Sort> registration = RegisterEntry<Sort>(module);
+
+  int SORTCODE, SORTPARAM, SORTNAME, SORTLEVEL;
+  bool compareLess = true;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -98,24 +82,18 @@ class ModuleSort
   int nvars;
 
   VarList varList1;
-  std::vector<VarInfo> varInfo;
+  std::vector<VarInfo> varsInfo;
   Varray2D<double> vardata;
   int operatorID;
 
-  int SORTCODE, SORTPARAM, SORTNAME, SORTLEVEL;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-  SORTCODE  = cdo_operator_add("sortcode",  0, 0, nullptr);
-  SORTPARAM = cdo_operator_add("sortparam", 0, 0, nullptr);
-  SORTNAME  = cdo_operator_add("sortname",  0, 0, nullptr);
-  SORTLEVEL = cdo_operator_add("sortlevel", 0, 0, nullptr);
-    // clang-format on
+    SORTCODE = module.get_id("sortcode");
+    SORTPARAM = module.get_id("sortparam");
+    SORTNAME = module.get_id("sortname");
+    SORTLEVEL = module.get_id("sortlevel");
 
     operatorID = cdo_operator_id();
 
@@ -124,7 +102,7 @@ public:
     if (operatorID == SORTLEVEL && cdo_operator_argc() == 1)
       {
         auto iarg = parameter_to_int(cdo_operator_argv(0));
-        if (iarg < 0) cmpvarlev = cmpvarlevelrev;
+        if (iarg < 0) compareLess = false;
       }
 
     streamID1 = cdo_open_read(0);
@@ -151,12 +129,12 @@ public:
 
     nvars = vlistNvars(vlistID1);
 
-    varInfo = std::vector<VarInfo>(nvars);
+    varsInfo = std::vector<VarInfo>(nvars);
     for (int varID = 0; varID < nvars; ++varID)
       {
         const auto &var = varList1[varID];
-        varInfo[varID].nlevs = var.nlevels;
-        varInfo[varID].levInfo.resize(var.nlevels);
+        varsInfo[varID].nlevels = var.nlevels;
+        varsInfo[varID].levInfo.resize(var.nlevels);
       }
 
     vardata = Varray2D<double>(nvars);
@@ -166,6 +144,7 @@ public:
         vardata[varID].resize(var.gridsize * var.nlevels);
       }
   }
+
   void
   run()
   {
@@ -186,22 +165,23 @@ public:
 
             if (tsID == 0)
               {
-                varInfo[varID].varID = varID;
-                varInfo[varID].code = var.code;
-                param_to_string(var.param, varInfo[varID].param, sizeof(varInfo[varID].param));
-                std::strcpy(varInfo[varID].name, var.name.c_str());
-                varInfo[varID].levInfo[levelID].levelID = levelID;
-                varInfo[varID].levInfo[levelID].level = cdo_zaxis_inq_level(var.zaxisID, levelID);
+                auto &varInfo = varsInfo[varID];
+                varInfo.varID = varID;
+                varInfo.code = var.code;
+                varInfo.param = param_to_string(var.param);
+                varInfo.name = var.name;
+                varInfo.levInfo[levelID].levelID = levelID;
+                varInfo.levInfo[levelID].level = cdo_zaxis_inq_level(var.zaxisID, levelID);
               }
 
             auto offset = var.gridsize * levelID;
             auto single = &vardata[varID][offset];
 
-            size_t nmiss;
-            cdo_read_record(streamID1, single, &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, single, &numMissVals);
 
-            setNmiss(varID, levelID, nvars, varInfo, nmiss);
-            // varInfo[varID].levInfo[levelID].nmiss = nmiss;
+            setNmiss(varID, levelID, nvars, varsInfo, numMissVals);
+            // varsInfo[varID].levInfo[levelID].numMissVals = numMissVals;
           }
 
         if (tsID == 0)
@@ -209,50 +189,55 @@ public:
             if (Options::cdoVerbose)
               for (int vindex = 0; vindex < nvars; vindex++)
                 {
-                  auto nlevels = varInfo[vindex].nlevs;
-                  for (int lindex = 0; lindex < nlevels; ++lindex)
-                    printf("sort in: %d %s %d %d %g\n", vindex, varInfo[vindex].name, varInfo[vindex].code, varInfo[vindex].nlevs,
-                           varInfo[vindex].levInfo[lindex].level);
+                  const auto &varInfo = varsInfo[vindex];
+                  for (int lindex = 0; lindex < varInfo.nlevels; ++lindex)
+                    printf("sort in: %d %s %d %d %g\n", vindex, varInfo.name.c_str(), varInfo.code, varInfo.nlevels,
+                           varInfo.levInfo[lindex].level);
                 }
 
             if (operatorID == SORTCODE)
-              std::sort(varInfo.begin(), varInfo.end(), cmpvarcode);
+              ranges::sort(varsInfo, {}, &VarInfo::code);
             else if (operatorID == SORTPARAM)
-              std::sort(varInfo.begin(), varInfo.end(), cmpvarparam);
+              ranges::sort(varsInfo, {}, &VarInfo::param);
             else if (operatorID == SORTNAME)
-              std::sort(varInfo.begin(), varInfo.end(), cmpvarname);
+              ranges::sort(varsInfo, {}, &VarInfo::name);
             else if (operatorID == SORTLEVEL)
               {
                 for (int vindex = 0; vindex < nvars; vindex++)
-                  std::sort(varInfo[vindex].levInfo.begin(), varInfo[vindex].levInfo.end(), cmpvarlev);
+                  {
+                    if (compareLess)
+                      ranges::sort(varsInfo[vindex].levInfo, ranges::less(), &LevInfo::level);
+                    else
+                      ranges::sort(varsInfo[vindex].levInfo, ranges::greater(), &LevInfo::level);
+                  }
               }
 
             if (Options::cdoVerbose)
               for (int vindex = 0; vindex < nvars; vindex++)
                 {
-                  auto nlevels = varInfo[vindex].nlevs;
-                  for (int lindex = 0; lindex < nlevels; ++lindex)
-                    printf("sort out: %d %s %d %d %g\n", vindex, varInfo[vindex].name, varInfo[vindex].code, varInfo[vindex].nlevs,
-                           varInfo[vindex].levInfo[lindex].level);
+                  const auto &varInfo = varsInfo[vindex];
+                  for (int lindex = 0; lindex < varInfo.nlevels; ++lindex)
+                    printf("sort out: %d %s %d %d %g\n", vindex, varInfo.name.c_str(), varInfo.code, varInfo.nlevels,
+                           varInfo.levInfo[lindex].level);
                 }
           }
 
         for (int vindex = 0; vindex < nvars; vindex++)
           {
-            auto varID = varInfo[vindex].varID;
-            const auto &var = varList1[varID];
+            const auto &varInfo = varsInfo[vindex];
+            const auto &var = varList1[varInfo.varID];
             for (int lindex = 0; lindex < var.nlevels; ++lindex)
               {
-                auto levelID = varInfo[vindex].levInfo[lindex].levelID;
-                auto nmiss = varInfo[vindex].levInfo[lindex].nmiss;
+                auto levelID = varInfo.levInfo[lindex].levelID;
+                auto numMissVals = varInfo.levInfo[lindex].numMissVals;
 
-                if (tsID == 0 || var.isConstant)
+                if (tsID == 0 || !var.isConstant)
                   {
                     auto offset = var.gridsize * levelID;
-                    auto single = &vardata[varID][offset];
+                    auto single = &vardata[varInfo.varID][offset];
 
-                    cdo_def_record(streamID2, varID, levelID);
-                    cdo_write_record(streamID2, single, nmiss);
+                    cdo_def_record(streamID2, varInfo.varID, levelID);
+                    cdo_write_record(streamID2, single, numMissVals);
                   }
               }
           }
@@ -260,21 +245,11 @@ public:
         tsID++;
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-void *
-Sort(void *process)
-{
-  ModuleSort sort;
-  sort.init(process);
-  sort.run();
-  sort.close();
-  return nullptr;
-}
diff --git a/src/Sorttimestamp.cc b/src/Sorttimestamp.cc
index 931a7fead4a2816f17889d0845cc9da937f16fab..52937e2c17183728225186e4dfa601b2f14da4d1 100644
--- a/src/Sorttimestamp.cc
+++ b/src/Sorttimestamp.cc
@@ -11,8 +11,6 @@
      Sorttimestamp    sorttimestamp         Sort all timesteps
 */
 
-#include <algorithm>  // sort
-
 #include <cdi.h>
 #include "julian_date.h"
 
@@ -30,13 +28,20 @@ struct TimeInfo
   double datetime;
 };
 
-static bool
-cmpdatetime(const TimeInfo &a, const TimeInfo &b)
-{
-  return a.datetime < b.datetime;
-}
-class ModuleSorttimestamp
+class Sorttimestamp : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Sorttimestamp",
+    .operators = { { "sorttimestamp"}, { "sorttaxis"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { -1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Sorttimestamp> registration = RegisterEntry<Sorttimestamp>(module);
+
   int varID, levelID;
   int lasttsID = -1;
   int nalloc = 0;
@@ -51,10 +56,8 @@ class ModuleSorttimestamp
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     skipSameTime = getenv_skip_same_time();
 
     if (cdo_operator_argc() == 1)
@@ -145,7 +148,7 @@ public:
         timeinfo[tsID].datetime = jdatetime;
       }
 
-    std::stable_sort(timeinfo.begin(), timeinfo.end(), cmpdatetime);
+    ranges::stable_sort(timeinfo, {}, &TimeInfo::datetime);
 
     vlistDefTaxis(vlistID2, taxisID2);
 
@@ -216,17 +219,5 @@ public:
   close()
   {
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Sorttimestamp(void *process)
-{
-  ModuleSorttimestamp sorttimestamp;
-  sorttimestamp.init(process);
-  sorttimestamp.run();
-  sorttimestamp.close();
-  return nullptr;
-}
diff --git a/src/Specinfo.cc b/src/Specinfo.cc
index b7f7adf257dcabe716cded479ddc5bea6d81ba46..e6c17c0ad72b4734683479eb9bba9d283496efbd 100644
--- a/src/Specinfo.cc
+++ b/src/Specinfo.cc
@@ -224,8 +224,20 @@ lookup_rl(long nsp, long *nroot, long *nlevel)
     }
 }
 
-class ModuleSpecinfo
+class Specinfo : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Specinfo",
+    .operators = { { "specinfo"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Specinfo> registration = RegisterEntry<Specinfo>(module);
+
   char arg[128], *parg;
   std::string argument;
   struct GridSpecifications
@@ -404,6 +416,7 @@ private:
 
     NLON_NLAT(p_grid_specs1, p_grid_specs2, p_grid_specs3);
   }
+
   void
   NLAT(GridSpecifications &p_grid_specs1, GridSpecifications &p_grid_specs2, GridSpecifications &p_grid_specs3)
   {
@@ -465,13 +478,12 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_input_arg("Txx, TLxx, NLON=xx, NLAT=xx, NIxx or ICONRyyLxx");
 
-    const long len = cdo_operator_argv(0).size();
+    long len = cdo_operator_argv(0).size();
 
     if ((len + 1) >= 128) cdo_abort("Parameter string too large!");
 
@@ -479,7 +491,7 @@ public:
     arg[len] = 0;
 
     argument = std::string(cdo_operator_argv(0));
-    std::transform(argument.begin(), argument.end(), argument.begin(), ::toupper);
+    ranges::transform(argument, argument.begin(), ::toupper);
   }
 
   void
@@ -529,16 +541,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-void *
-Specinfo(void *process)
-{
-  ModuleSpecinfo specinfo;
-  specinfo.init(process);
-  specinfo.run();
-  specinfo.close();
-
-  return nullptr;
-}
diff --git a/src/Spectral.cc b/src/Spectral.cc
index f185f857840e0cf2d9fdfff9bb4ce3e70b83558f..3b56d1dc09e2455198967b45ba0f9d5fd718e79d 100644
--- a/src/Spectral.cc
+++ b/src/Spectral.cc
@@ -95,8 +95,25 @@ sp2gp_init(SP_Transformation &spTrans, int gridID1, int gridIDsp, int gridIDgp,
   return gridID2;
 }
 
-class ModuleSpectral
+class Spectral : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Spectral",
+    .operators = { { "gp2sp", SpectralHelp },
+                   { "gp2spl", SpectralHelp },
+                   { "sp2gp", SpectralHelp },
+                   { "sp2gpl", SpectralHelp },
+                   { "sp2sp", SpecconvHelp },
+                   { "spcut", SpecconvHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Spectral> registration = RegisterEntry<Spectral>(module);
+  int GP2SP, GP2SPL, SP2GP, SP2GPL, SP2SP, SPCUT;
 
   int gridID1 = -1, gridID2 = -1;
   int defaultTrunc = 0;
@@ -122,28 +139,20 @@ class ModuleSpectral
   bool lsp2gp;
   bool linear;
 
-  int GP2SP;
-  int GP2SPL;
-  int SP2GP;
-  int SP2GPL;
-  int SP2SP;
-  int SPCUT;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     dataIsUnchanged = data_is_unchanged();
 
     // clang-format off
-    GP2SP  = cdo_operator_add("gp2sp",  0, 0, nullptr);
-    GP2SPL = cdo_operator_add("gp2spl", 0, 0, nullptr);
-    SP2GP  = cdo_operator_add("sp2gp",  0, 0, nullptr);
-    SP2GPL = cdo_operator_add("sp2gpl", 0, 0, nullptr);
-    SP2SP  = cdo_operator_add("sp2sp",  0, 0, nullptr);
-    SPCUT  = cdo_operator_add("spcut",  0, 0, nullptr);
+GP2SP = module.get_id("gp2sp");
+GP2SPL = module.get_id("gp2spl");
+SP2GP = module.get_id("sp2gp");
+SP2GPL = module.get_id("sp2gpl");
+SP2SP = module.get_id("sp2sp");
+SPCUT = module.get_id("spcut");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -285,9 +294,9 @@ public:
 
             if (processVars[varID])
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array1.data(), &nmiss);
-                if (nmiss) cdo_abort("Missing values unsupported for spectral data!");
+                size_t numMissVals;
+                cdo_read_record(streamID1, array1.data(), &numMissVals);
+                if (numMissVals) cdo_abort("Missing values unsupported for spectral data!");
 
                 gridID1 = vlistInqVarGrid(vlistID1, varID);
                 // clang-format off
@@ -298,7 +307,7 @@ public:
                 // clang-format on
 
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, array2.data(), nmiss);
+                cdo_write_record(streamID2, array2.data(), numMissVals);
               }
             else
               {
@@ -306,9 +315,9 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    size_t nmiss;
-                    cdo_read_record(streamID1, array1.data(), &nmiss);
-                    cdo_write_record(streamID2, array1.data(), nmiss);
+                    size_t numMissVals;
+                    cdo_read_record(streamID1, array1.data(), &numMissVals);
+                    cdo_write_record(streamID2, array1.data(), numMissVals);
                   }
               }
           }
@@ -322,18 +331,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Spectral(void *process)
-{
-  ModuleSpectral spectral;
-  spectral.init(process);
-  spectral.run();
-  spectral.close();
-
-  return nullptr;
-}
diff --git a/src/Spectrum.cc b/src/Spectrum.cc
index e76fb2c8cf43048dcc75858ae45d5cab858f6c8a..61a8352900b3b500f9e32ff9e18b95a1d918f54b 100644
--- a/src/Spectrum.cc
+++ b/src/Spectrum.cc
@@ -127,12 +127,23 @@ spectrum(int nrec, double *data, double *spectrum, double *real, double *imag, c
   if (!(seg_l & 1)) spectrum[seg_l / 2] *= 2;
 }
 
-class ModuleSpectrum
+class Spectrum : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Spectrum",
+    .operators = { { "spectrum"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Spectrum> registration = RegisterEntry<Spectrum>(module);
   int varID, levelID;
   int k;
   int nalloc = 0;
-  size_t nmiss;
+  size_t numMissVals;
   int freq;
   int nvars;
 
@@ -149,9 +160,8 @@ class ModuleSpectrum
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     streamID1 = cdo_open_read(0);
 
@@ -196,10 +206,10 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             const auto &var = varList[varID];
             vars[tsID][varID][levelID].resize(var.gridsize);
-            cdo_read_record(streamID1, vars[tsID][varID][levelID].vec_d.data(), &nmiss);
-            vars[tsID][varID][levelID].nmiss = nmiss;
+            cdo_read_record(streamID1, vars[tsID][varID][levelID].vec_d.data(), &numMissVals);
+            vars[tsID][varID][levelID].numMissVals = numMissVals;
 
-            if (nmiss) cdo_abort("Missing values are not allowed!");
+            if (numMissVals) cdo_abort("Missing values are not allowed!");
           }
 
         tsID++;
@@ -296,7 +306,7 @@ public:
               {
                 if (!vars2[tsID][varID][levelID].empty())
                   {
-                    nmiss = vars2[tsID][varID][levelID].nmiss;
+                    numMissVals = vars2[tsID][varID][levelID].numMissVals;
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, vars2[tsID][varID][levelID].vec_d.data(), 0);
                   }
@@ -309,17 +319,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Spectrum(void *process)
-{
-  ModuleSpectrum spectrum;
-  spectrum.init(process);
-  spectrum.run();
-  spectrum.close();
-
-  return nullptr;
-}
diff --git a/src/Split.cc b/src/Split.cc
index 86a2160d7c29557e3133aac765670f4fa7fe2b86..808b239883fd42235b83f07a347e57de39d38467 100644
--- a/src/Split.cc
+++ b/src/Split.cc
@@ -164,7 +164,7 @@ split_name(bool swapObase, const std::string &fileSuffix, std::string &fileName,
       cdo_vlist_copy_flag(vlistID2, vlistID1);
       vlistIDs[index] = vlistID2;
 
-      std::string formatted =  fileName + var.name;
+      std::string formatted = fileName + var.name;
       gen_filename(formatted, swapObase, cdo_get_obase(), fileSuffix);
 
       streamIDs[index] = cdo_open_write(formatted);
@@ -304,7 +304,6 @@ split_zaxis(bool swapObase, const std::string &fileSuffix, std::string &fileName
       cdo_vlist_copy_flag(vlistID2, vlistID1);
       vlistIDs[index] = vlistID2;
 
-
       std::string formatted = fileName + string_format("%02d", vlistZaxisIndex(vlistID1, zaxisIDs[index]) + 1);
       gen_filename(formatted, swapObase, cdo_get_obase(), fileSuffix);
 
@@ -365,15 +364,28 @@ split_tabnum(bool swapObase, const std::string &fileSuffix, std::string &fileNam
   return nsplit;
 }
 
-class ModuleSplit
+class Split : public Process
 {
-  int SPLITCODE;
-  int SPLITPARAM;
-  int SPLITNAME;
-  int SPLITLEVEL;
-  int SPLITGRID;
-  int SPLITZAXIS;
-  int SPLITTABNUM;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Split",
+    .operators = { { "splitcode", SplitHelp },
+                   { "splitparam", SplitHelp },
+                   { "splitname", SplitHelp },
+                   { "splitlevel", SplitHelp },
+                   { "splitgrid", SplitHelp },
+                   { "splitzaxis", SplitHelp },
+                   { "splittabnum", SplitHelp } },
+    .aliases = { { "splitvar", "splitname" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+
+  inline static RegisterEntry<Split> registration = RegisterEntry<Split>(module);
+
+  int SPLITCODE, SPLITPARAM, SPLITNAME, SPLITLEVEL, SPLITGRID, SPLITZAXIS, SPLITTABNUM;
 
   std::vector<int> vlistIDs;
   std::vector<CdoStreamID> streamIDs;
@@ -387,23 +399,19 @@ class ModuleSplit
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
-
     dataIsUnchanged = data_is_unchanged();
 
     // clang-format off
-    SPLITCODE   = cdo_operator_add("splitcode",   0, 0, nullptr);
-    SPLITPARAM  = cdo_operator_add("splitparam",  0, 0, nullptr);
-    SPLITNAME   = cdo_operator_add("splitname",   0, 0, nullptr);
-    SPLITLEVEL  = cdo_operator_add("splitlevel",  0, 0, nullptr);
-    SPLITGRID   = cdo_operator_add("splitgrid",   0, 0, nullptr);
-    SPLITZAXIS  = cdo_operator_add("splitzaxis",  0, 0, nullptr);
-    SPLITTABNUM = cdo_operator_add("splittabnum", 0, 0, nullptr);
+SPLITCODE = module.get_id("splitcode");
+SPLITPARAM = module.get_id("splitparam");
+SPLITNAME = module.get_id("splitname");
+SPLITLEVEL = module.get_id("splitlevel");
+SPLITGRID = module.get_id("splitgrid");
+SPLITZAXIS = module.get_id("splitzaxis");
+SPLITTABNUM = module.get_id("splittabnum");
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -512,18 +520,5 @@ public:
 
     for (auto &streamID : streamIDs) cdo_stream_close(streamID);
     for (auto &id : vlistIDs) vlistDestroy(id);
-
-    cdo_finish();
   }
 };
-
-void *
-Split(void *process)
-{
-  ModuleSplit split;
-  split.init(process);
-  split.run();
-  split.close();
-
-  return nullptr;
-}
diff --git a/src/Splitdate.cc b/src/Splitdate.cc
index 9e177edf1c9af534734143aaee50e7616d56ec25..9c4d7d10eed36f03b9cc2b3cf6b78074d4dfe469 100644
--- a/src/Splitdate.cc
+++ b/src/Splitdate.cc
@@ -19,8 +19,20 @@
 #include "util_files.h"
 #include "util_string.h"
 
-class ModuleSplitdate
+class Splitdate : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Splitdate",
+    .operators = { { "splitdate", SplitdateHelp }, { "splitdatetime", SplitdateHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Splitdate> registration = RegisterEntry<Splitdate>(module);
+  int SPLITDATE;
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -40,16 +52,12 @@ class ModuleSplitdate
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
 
     dataIsUnchanged = data_is_unchanged();
 
-    auto SPLITDATE = cdo_operator_add("splitdate", 0, 0, nullptr);
-    cdo_operator_add("splitdatetime", 0, 0, nullptr);
+    SPLITDATE = module.get_id("splitdate");
 
     auto operatorID = cdo_operator_id();
     splitDate = (operatorID == SPLITDATE);
@@ -147,7 +155,8 @@ public:
             cdiDate_decode(vDateTime.date, &year, &month, &day);
             int hour, minute, second, ms;
             cdiTime_decode(vDateTime.time, &hour, &minute, &second, &ms);
-            auto fileName = cdo_get_obase() + string_format("%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hour, minute, second);
+            auto fileName
+                = cdo_get_obase() + string_format("%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hour, minute, second);
             if (fileSuffix.size() > 0) fileName += fileSuffix;
 
             if (Options::cdoVerbose) cdo_print("create file %s", fileName);
@@ -169,8 +178,8 @@ public:
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
                         cdo_def_record(streamID2, varID, levelID);
-                        auto nmiss = vars[varID][levelID].nmiss;
-                        cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), nmiss);
+                        auto numMissVals = vars[varID][levelID].numMissVals;
+                        cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), numMissVals);
                       }
                   }
               }
@@ -185,9 +194,9 @@ public:
             if (dataIsUnchanged && !(tsID == 0 && haveConstVars)) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array.data(), &nmiss);
-                cdo_write_record(streamID2, array.data(), nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID1, array.data(), &numMissVals);
+                cdo_write_record(streamID2, array.data(), numMissVals);
 
                 if (tsID == 0 && haveConstVars)
                   {
@@ -195,7 +204,7 @@ public:
                     if (var.isConstant)
                       {
                         varray_copy(var.gridsize, array, vars[varID][levelID].vec_d);
-                        vars[varID][levelID].nmiss = nmiss;
+                        vars[varID][levelID].numMissVals = numMissVals;
                       }
                   }
               }
@@ -214,18 +223,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Splitdate(void *process)
-{
-  ModuleSplitdate splitdate;
-  splitdate.init(process);
-  splitdate.run();
-  splitdate.close();
-
-  return nullptr;
-}
diff --git a/src/Splitrec.cc b/src/Splitrec.cc
index 0a45d85d172225ecf3ba672ae09fdd685385a22b..7ee57a464295539d5f0d05f342f8fe062e5c17f5 100644
--- a/src/Splitrec.cc
+++ b/src/Splitrec.cc
@@ -19,8 +19,19 @@
 #include "util_files.h"
 #include "util_string.h"
 
-class ModuleSplitrec
+class Splitrec : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Splitrec",
+    .operators = { { "splitrec", SplitHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Splitrec> registration = RegisterEntry<Splitrec>(module);
 
   CdoStreamID streamID1;
   int vlistID1;
@@ -31,11 +42,8 @@ class ModuleSplitrec
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
 
     operator_check_argc(0);
 
@@ -104,16 +112,5 @@ public:
   close()
   {
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Splitrec(void *process)
-{
-  ModuleSplitrec splitrec;
-  splitrec.init(process);
-  splitrec.run();
-  splitrec.close();
-  return nullptr;
-}
diff --git a/src/Splitsel.cc b/src/Splitsel.cc
index a2438a85f738e367e6ea0518f70fe3f6cd7605b3..a50e891747122ecf1a35094aa4efcb003a8c333e 100644
--- a/src/Splitsel.cc
+++ b/src/Splitsel.cc
@@ -19,8 +19,19 @@
 #include "util_files.h"
 #include "util_string.h"
 
-class ModuleSplitsel
+class Splitsel : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Splitsel",
+    .operators = { { "splitsel", SplitselHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Splitsel> registration = RegisterEntry<Splitsel>(module);
 
   int noffset;
   CdoStreamID streamID1;
@@ -43,16 +54,11 @@ class ModuleSplitsel
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
 
     dataIsUnchanged = data_is_unchanged();
 
-    cdo_operator_add("splitsel", 0, 0, nullptr);
-
     // operator_input_arg("numSets <noffset <numSkip>>");
 
     auto nargc = cdo_operator_argc();
@@ -143,7 +149,7 @@ public:
               if (var.isConstant)
                 {
                   auto &field = fields[varID][levelID];
-                  cdo_read_record(streamID1, field.vec_d.data(), &field.nmiss);
+                  cdo_read_record(streamID1, field.vec_d.data(), &field.numMissVals);
                 }
             }
       }
@@ -185,7 +191,7 @@ public:
                           {
                             cdo_def_record(streamID2, varID, levelID);
                             auto &field = fields[varID][levelID];
-                            cdo_write_record(streamID2, field.vec_d.data(), field.nmiss);
+                            cdo_write_record(streamID2, field.vec_d.data(), field.numMissVals);
                           }
                       }
                   }
@@ -200,9 +206,9 @@ public:
                 if (dataIsUnchanged && !(tsID == 0 && haveConstVars)) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    size_t nmiss;
-                    cdo_read_record(streamID1, array.data(), &nmiss);
-                    cdo_write_record(streamID2, array.data(), nmiss);
+                    size_t numMissVals;
+                    cdo_read_record(streamID1, array.data(), &numMissVals);
+                    cdo_write_record(streamID2, array.data(), numMissVals);
 
                     if (tsID == 0 && haveConstVars)
                       {
@@ -211,7 +217,7 @@ public:
                           {
                             auto &field = fields[varID][levelID];
                             varray_copy(var.gridsize, array, field.vec_d);
-                            field.nmiss = nmiss;
+                            field.numMissVals = numMissVals;
                           }
                       }
                   }
@@ -244,18 +250,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Splitsel(void *process)
-{
-  ModuleSplitsel splitsel;
-  splitsel.init(process);
-  splitsel.run();
-  splitsel.close();
-
-  return nullptr;
-}
diff --git a/src/Splittime.cc b/src/Splittime.cc
index 3f326abbb024509419b4722e1c3684f7b69cab57..f819a12280df8fdda2d66086572ec441c535d33d 100644
--- a/src/Splittime.cc
+++ b/src/Splittime.cc
@@ -45,8 +45,29 @@ datetime_to_tm(CdiDateTime vDateTime)
   return stime;
 }
 
-class ModuleSplitTime
+class Splittime : public Process
 {
+  enum
+  {
+    func_time,
+    func_date
+  };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Splittime",
+    .operators = { { "splithour", func_time, 10000, SplittimeHelp },
+                   { "splitday", func_date, 1, SplittimeHelp },
+                   { "splitmon", func_date, 100, SplittimeHelp },
+                   { "splitseas", func_date, 100, SplittimeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Splittime> registration = RegisterEntry<Splittime>(module);
+  int SPLITMON, SPLITSEAS;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamIDs[MaxStreams];
@@ -65,9 +86,6 @@ class ModuleSplitTime
   std::string fileSuffix;
   const char *format = nullptr;
 
-  int SPLITMON;
-  int SPLITSEAS;
-
   VarList varList1;
   FieldVector2D vars;
   Varray<double> array;
@@ -75,27 +93,16 @@ class ModuleSplitTime
   bool haveConstVars;
   bool dataIsUnchanged;
 
-  enum
-  {
-    func_time,
-    func_date
-  };
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
 
     dataIsUnchanged = data_is_unchanged();
 
     // clang-format off
-                cdo_operator_add("splithour", func_time, 10000, nullptr);
-                cdo_operator_add("splitday",  func_date,     1, nullptr);
-    SPLITMON  = cdo_operator_add("splitmon",  func_date,   100, nullptr);
-    SPLITSEAS = cdo_operator_add("splitseas", func_date,   100, nullptr);
+SPLITMON = module.get_id("splitmon");
+SPLITSEAS = module.get_id("splitseas");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -175,10 +182,7 @@ public:
 
             if (operatorID == SPLITSEAS) index = month_to_season(index);
           }
-        else if (operfunc == func_time)
-          {
-            index = (cdiTime_get(vDateTime.time) / operintval) % 100;
-          }
+        else if (operfunc == func_time) { index = (cdiTime_get(vDateTime.time) / operintval) % 100; }
 
         if (index < 0 || index >= MaxStreams) cdo_abort("Index out of range!");
 
@@ -228,8 +232,8 @@ public:
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
                         cdo_def_record(streamID2, varID, levelID);
-                        auto nmiss = vars[varID][levelID].nmiss;
-                        cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), nmiss);
+                        auto numMissVals = vars[varID][levelID].numMissVals;
+                        cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), numMissVals);
                       }
                   }
               }
@@ -244,9 +248,9 @@ public:
             if (dataIsUnchanged && !(tsID == 0 && haveConstVars)) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array.data(), &nmiss);
-                cdo_write_record(streamID2, array.data(), nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID1, array.data(), &numMissVals);
+                cdo_write_record(streamID2, array.data(), numMissVals);
 
                 if (tsID == 0 && haveConstVars)
                   {
@@ -254,7 +258,7 @@ public:
                     if (var.isConstant)
                       {
                         varray_copy(var.gridsize, array, vars[varID][levelID].vec_d);
-                        vars[varID][levelID].nmiss = nmiss;
+                        vars[varID][levelID].numMissVals = numMissVals;
                       }
                   }
               }
@@ -276,18 +280,5 @@ public:
       }
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Splittime(void *process)
-{
-  ModuleSplitTime splittime;
-  splittime.init(process);
-  splittime.run();
-  splittime.close();
-
-  return nullptr;
-}
diff --git a/src/Splityear.cc b/src/Splityear.cc
index c1e53028d4f604c65eb19de92c1e0c19615d15f7..0d8f6ca169d68d10490f0517ed598dca265310aa 100644
--- a/src/Splityear.cc
+++ b/src/Splityear.cc
@@ -22,8 +22,20 @@
 
 #define MAX_YEARS 99999
 
-class ModuleSplitYear
+class Splityear : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Splityear",
+    .operators = { { "splityear", SplittimeHelp }, { "splityearmon", SplittimeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, OBASE, OnlyFirst },
+  };
+  inline static RegisterEntry<Splityear> registration = RegisterEntry<Splityear>(module);
+  int SPLITYEAR, SPLITYEARMON;
   int operatorID;
   CdoStreamID streamID1;
   Varray<int> cyear = Varray<int>(MAX_YEARS, 0);
@@ -32,8 +44,6 @@ class ModuleSplitYear
   int taxisID2;
   int vlistID2;
   CdoStreamID streamID2 = CDO_STREAM_UNDEF;
-  int SPLITYEAR;
-  int SPLITYEARMON;
   Varray<double> array;
   VarList varList1;
   bool haveConstVars;
@@ -42,18 +52,14 @@ class ModuleSplitYear
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
-    if (process_self().m_ID != 0) cdo_abort("This operator can't be combined with other operators!");
-
     dataIsUnchanged = data_is_unchanged();
 
     // clang-format off
-    SPLITYEAR    = cdo_operator_add("splityear",     0, 0, nullptr);
-    SPLITYEARMON = cdo_operator_add("splityearmon",  0, 0, nullptr);
+SPLITYEAR = module.get_id("splityear");
+SPLITYEARMON = module.get_id("splityearmon");
     // clang-format on
     operatorID = cdo_operator_id();
 
@@ -186,9 +192,9 @@ public:
                   {
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
-                        auto nmiss = vars[varID][levelID].nmiss;
+                        auto numMissVals = vars[varID][levelID].numMissVals;
                         cdo_def_record(streamID2, varID, levelID);
-                        cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), nmiss);
+                        cdo_write_record(streamID2, vars[varID][levelID].vec_d.data(), numMissVals);
                       }
                   }
               }
@@ -203,9 +209,9 @@ public:
             if (dataIsUnchanged && !(tsID == 0 && haveConstVars)) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                size_t nmiss;
-                cdo_read_record(streamID1, array.data(), &nmiss);
-                cdo_write_record(streamID2, array.data(), nmiss);
+                size_t numMissVals;
+                cdo_read_record(streamID1, array.data(), &numMissVals);
+                cdo_write_record(streamID2, array.data(), numMissVals);
 
                 if (tsID == 0 && haveConstVars)
                   {
@@ -213,7 +219,7 @@ public:
                     if (var.isConstant)
                       {
                         varray_copy(var.gridsize, array, vars[varID][levelID].vec_d);
-                        vars[varID][levelID].nmiss = nmiss;
+                        vars[varID][levelID].numMissVals = numMissVals;
                       }
                   }
               }
@@ -229,17 +235,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-void *
-Splityear(void *process)
-{
-  ModuleSplitYear splitYear;
-  splitYear.init(process);
-  splitYear.run();
-  splitYear.close();
-
-  return nullptr;
-}
diff --git a/src/Tee.cc b/src/Tee.cc
index a5cae93a95e3298d4c63f636cb96d82aa5d9ce60..51d58cd81a044a65fdeb411ceaed2e67f9aa0979 100644
--- a/src/Tee.cc
+++ b/src/Tee.cc
@@ -9,8 +9,19 @@
 
 #include "process_int.h"
 
-class ModuleTee
+class Tee : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Tee",
+    .operators = { { "tee", TeeHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Tee> registration = RegisterEntry<Tee>(module);
   Field field;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -22,9 +33,8 @@ class ModuleTee
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
     operator_check_argc(1);
 
     streamID1 = cdo_open_read(0);
@@ -78,9 +88,9 @@ public:
 
             streamDefRecord(streamID3, varID, levelID);
             if (field.memType == MemType::Float)
-              streamWriteRecordF(streamID3, field.vec_f.data(), field.nmiss);
+              streamWriteRecordF(streamID3, field.vec_f.data(), field.numMissVals);
             else
-              streamWriteRecord(streamID3, field.vec_d.data(), field.nmiss);
+              streamWriteRecord(streamID3, field.vec_d.data(), field.numMissVals);
           }
 
         tsID++;
@@ -93,18 +103,5 @@ public:
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
     streamClose(streamID3);
-
-    cdo_finish();
   }
 };
-
-void *
-Tee(void *process)
-{
-  ModuleTee tee;
-  tee.init(process);
-  tee.run();
-  tee.close();
-
-  return nullptr;
-}
diff --git a/src/Templates.cc b/src/Templates.cc
index 21fb6cdf648bd492ed3f683ebdd28b81fdba8de8..5e7e79123a8530bf77ba208707b8360c70c3878f 100644
--- a/src/Templates.cc
+++ b/src/Templates.cc
@@ -9,7 +9,6 @@
 
 #include "process_int.h"
 
-class ModuleTemplate1
 {
   CdoStreamID streamID1;
   int taxisID1;
@@ -18,16 +17,14 @@ class ModuleTemplate1
   int taxisID2;
   int vlistID2;
 
-  size_t nmiss;
+  size_t numMissVals;
   bool dataIsUnchanged;
 
   Varray<double> array;
 
 public:
-  void
-  init(void *process)
+  void init()
   {
-    cdo_initialize(process);
 
     dataIsUnchanged = data_is_unchanged();
 
@@ -51,8 +48,7 @@ public:
       }
   }
 
-  void
-  run()
+  void run()
   {
     int tsID = 0;
     while (true)
@@ -72,29 +68,25 @@ public:
             if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                cdo_read_record(streamID1, array.data(), &nmiss);
-                cdo_write_record(streamID2, array.data(), nmiss);
+                cdo_read_record(streamID1, array.data(), &numMissVals);
+                cdo_write_record(streamID2, array.data(), numMissVals);
               }
           }
 
         tsID++;
       }
   }
-  void
-  close()
+  void close()
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
 
-class ModuleTemplate2
 {
-  size_t nmiss;
+  size_t numMissVals;
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -106,10 +98,8 @@ class ModuleTemplate2
   Varray<double> array;
 
 public:
-  void
-  init(void *process)
+  void init()
   {
-    cdo_initialize(process);
 
     streamID1 = cdo_open_read(0);
 
@@ -127,8 +117,7 @@ public:
     const auto gridsizemax = vlistGridsizeMax(vlistID1);
     array = Varray<double>(gridsizemax);
   }
-  void
-  run()
+  void run()
   {
     int tsID = 0;
     while (true)
@@ -145,21 +134,18 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            cdo_read_record(streamID1, array.data(), &nmiss);
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_read_record(streamID1, array.data(), &numMissVals);
+            cdo_write_record(streamID2, array.data(), numMissVals);
           }
 
         tsID++;
       }
   }
-  void
-  close()
+  void close()
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
diff --git a/src/Test.cc b/src/Test.cc
index 4d9ee576cf185caed68b8b4dd7120bafce817fa6..568b343d19f49be0e9082a8aebb55ff65b7d52ee 100644
--- a/src/Test.cc
+++ b/src/Test.cc
@@ -10,8 +10,18 @@
 #include "process_int.h"
 #include "varray.h"
 
-class TestModuleTestdata
+class Testdata : public Process
 {
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Testdata",
+    .operators = { { "testdata" } },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Testdata> registration = RegisterEntry<Testdata>(module);
   CdoStreamID streamID1;
   int taxisID1;
   int vlistID1;
@@ -30,9 +40,8 @@ class TestModuleTestdata
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -77,8 +86,8 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            size_t nmiss;
-            cdo_read_record(streamID1, array.data(), &nmiss);
+            size_t numMissVals;
+            cdo_read_record(streamID1, array.data(), &numMissVals);
 
             gridsize = gridInqSize(vlistInqVarGrid(vlistID1, varID));
             for (size_t i = 0; i < gridsize; ++i)
@@ -98,7 +107,7 @@ public:
                          (unsigned int) cval[4 * i + 2], (unsigned int) cval[4 * i + 3], ival[i], fval[i]);
               }
 
-            cdo_write_record(streamID2, array.data(), nmiss);
+            cdo_write_record(streamID2, array.data(), numMissVals);
 
             fwrite(cval.data(), 4, gridsize, fp);
           }
@@ -113,19 +122,5 @@ public:
     std::fclose(fp);
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Testdata(void *process)
-{
-  TestModuleTestdata testdata;
-
-  testdata.init(process);
-  testdata.run();
-  testdata.close();
-
-  return nullptr;
-}
diff --git a/src/Tests.cc b/src/Tests.cc
index 999d64a4d7a50917c2345daf16285a2552029cfc..15d36625212c388b32b06b7339e84c8fbdb606b4 100644
--- a/src/Tests.cc
+++ b/src/Tests.cc
@@ -11,8 +11,25 @@
 #include "param_conversion.h"
 #include "statistic.h"
 
-class ModuleTests
+class Tests : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Tests",
+    .operators = { { "normal" },
+                   { "studentt", 0, 0, "degree of freedom" },
+                   { "chisquare", 0, 0, "degree of freedom" },
+                   { "beta", 0, 0, "p and q" },
+                   { "fisher", 0, 0, "degree of freedom of nominator and of denominator" } },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Tests> registration = RegisterEntry<Tests>(module);
+
+  int NORMAL, STUDENTT, CHISQUARE, BETA, FISHER;
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -27,21 +44,15 @@ class ModuleTests
   Varray<double> array1, array2;
   VarList varList1;
 
-  int NORMAL, STUDENTT, CHISQUARE, BETA, FISHER;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-   NORMAL    = cdo_operator_add("normal",    0, 0, nullptr);
-   STUDENTT  = cdo_operator_add("studentt",  0, 0, "degree of freedom");
-   CHISQUARE = cdo_operator_add("chisquare", 0, 0, "degree of freedom");
-   BETA      = cdo_operator_add("beta",      0, 0, "p and q");
-   FISHER    = cdo_operator_add("fisher",    0, 0, "degree of freedom of nominator and of denominator");
-    // clang-format on
+    NORMAL = module.get_id("normal");
+    STUDENTT = module.get_id("studentt");
+    CHISQUARE = module.get_id("chisquare");
+    BETA = module.get_id("beta");
+    FISHER = module.get_id("fisher");
 
     operatorID = cdo_operator_id();
 
@@ -94,6 +105,7 @@ public:
 
     varList_init(varList1, vlistID1);
   }
+
   void
   run()
   {
@@ -109,9 +121,9 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
-            size_t nmiss;
+            size_t numMissVals;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto gridsize = varList1[varID].gridsize;
             auto missval = varList1[varID].missval;
@@ -148,12 +160,13 @@ public:
             else { cdo_abort("Internal problem, operator not implemented!"); }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
       }
   }
+
   void
   close()
   {
@@ -161,17 +174,5 @@ public:
     cdo_stream_close(streamID2);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Tests(void *process)
-{
-  ModuleTests tests;
-  tests.init(process);
-  tests.run();
-  tests.close();
-  return nullptr;
-}
diff --git a/src/Timcount.cc b/src/Timcount.cc
index 3e9a63a70b86c29e9a9b6b34d2d492c176953bf0..6cdf57c38de2adb408356f9e964a67f801170b24 100644
--- a/src/Timcount.cc
+++ b/src/Timcount.cc
@@ -24,8 +24,23 @@
 #include "util_date.h"
 #include "field_functions.h"
 
-class ModuleTimcount
+class Timcount : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timcount",
+    .operators = { { "timcount", 0, CMP_DATE, nullptr},
+                   { "yearcount", 0, CMP_YEAR, nullptr},
+                   { "moncount", 0, CMP_MONTH, nullptr},
+                   { "daycount", 0, CMP_DAY, nullptr},
+                   { "hourcount", 0, CMP_HOUR, nullptr} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timcount> registration = RegisterEntry<Timcount>(module);
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -45,16 +60,10 @@ class ModuleTimcount
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-  cdo_operator_add("timcount",  0, CMP_DATE, nullptr);
-  cdo_operator_add("yearcount", 0, CMP_YEAR, nullptr);
-  cdo_operator_add("moncount",  0, CMP_MONTH, nullptr);
-  cdo_operator_add("daycount",  0, CMP_DAY, nullptr);
-  cdo_operator_add("hourcount", 0, CMP_HOUR, nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -122,7 +131,7 @@ public:
                 if (numSets == 0)
                   {
                     for (size_t i = 0; i < fieldsize; ++i) vars1[varID][levelID].vec_d[i] = vars1[varID][levelID].missval;
-                    vars1[varID][levelID].nmiss = fieldsize;
+                    vars1[varID][levelID].numMissVals = fieldsize;
                   }
 
                 field.init(varList1[varID]);
@@ -147,7 +156,7 @@ public:
             if (otsID && varList1[varID].isConstant) continue;
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, vars1[varID][levelID].vec_d.data(), vars1[varID][levelID].nmiss);
+            cdo_write_record(streamID2, vars1[varID][levelID].vec_d.data(), vars1[varID][levelID].numMissVals);
           }
 
         if (nrecs == 0) break;
@@ -160,19 +169,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timcount(void *process)
-{
-  ModuleTimcount timcount;
-
-  timcount.init(process);
-  timcount.run();
-  timcount.close();
-
-  return nullptr;
-}
diff --git a/src/Timcumsum.cc b/src/Timcumsum.cc
index cca35e19f189ae2102b56f5ae68cb2bc2e84f9dd..8792c1f9a6d86eb888a76a0e2118c19452ac2a5b 100644
--- a/src/Timcumsum.cc
+++ b/src/Timcumsum.cc
@@ -16,8 +16,19 @@
 #include "process_int.h"
 #include "field_functions.h"
 
-class ModuleTimcumsum
+class Timcumsum : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timcumsum",
+    .operators = { { "timcumsum", TimcumsumHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timcumsum> registration = RegisterEntry<Timcumsum>(module);
   CdoStreamID streamID1;
   int taxisID1;
   CdoStreamID streamID2;
@@ -30,9 +41,8 @@ class ModuleTimcumsum
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -77,19 +87,19 @@ public:
 
             if (tsID == 0)
               {
-                cdo_read_record(streamID1, rvars1.vec_d.data(), &rvars1.nmiss);
-                if (rvars1.nmiss)
+                cdo_read_record(streamID1, rvars1.vec_d.data(), &rvars1.numMissVals);
+                if (rvars1.numMissVals)
                   for (size_t i = 0; i < fieldsize; ++i)
                     if (DBL_IS_EQUAL(rvars1.vec_d[i], rvars1.missval)) rvars1.vec_d[i] = 0;
               }
             else
               {
-                cdo_read_record(streamID1, field.vec_d.data(), &field.nmiss);
+                cdo_read_record(streamID1, field.vec_d.data(), &field.numMissVals);
                 field.size = fieldsize;
                 field.grid = rvars1.grid;
                 field.missval = rvars1.missval;
 
-                if (field.nmiss)
+                if (field.numMissVals)
                   for (size_t i = 0; i < fieldsize; ++i)
                     if (DBL_IS_EQUAL(field.vec_d[i], rvars1.missval)) field.vec_d[i] = 0;
 
@@ -97,7 +107,7 @@ public:
               }
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1.vec_d.data(), rvars1.nmiss);
+            cdo_write_record(streamID2, rvars1.vec_d.data(), rvars1.numMissVals);
           }
 
         tsID++;
@@ -108,19 +118,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timcumsum(void *process)
-{
-  ModuleTimcumsum timcumsum;
-
-  timcumsum.init(process);
-  timcumsum.run();
-  timcumsum.close();
-
-  return nullptr;
-}
diff --git a/src/Timfillmiss.cc b/src/Timfillmiss.cc
index a7a88eb1813be877aa95add0dceb8724eb1319d9..9c223f2738f1b2eee894789a404fc67988524361 100644
--- a/src/Timfillmiss.cc
+++ b/src/Timfillmiss.cc
@@ -28,8 +28,20 @@ julianDate_to_double(int calendar, const CdiDateTime &dateTime1, const CdiDateTi
          / 86400.0;
 }
 
-class ModuleTimfillmiss
+class Timfillmiss : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timfillmiss",
+    .operators = { { "timfillmiss", TimfillmissHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timfillmiss> registration = RegisterEntry<Timfillmiss>(module);
+
 private:
   DateTimeList dtlist;
   CdoStreamID streamID1;
@@ -55,12 +67,6 @@ private:
   Varray<double> timeValues;
   int numSteps;
 
-  void
-  add_operators()
-  {
-    cdo_operator_add("timfillmiss", 0, 0, nullptr);
-  }
-
   FillMethod
   convert_fillmethod(const std::string &methodStr)
   {
@@ -81,7 +87,7 @@ private:
 
         KVList kvlist;
         kvlist.name = cdo_module_name();
-        if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+        if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
         if (Options::cdoVerbose) kvlist.print();
 
         for (const auto &kv : kvlist)
@@ -103,12 +109,8 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
-
     get_parameter();
     limit = std::max(limit, 0);
     maxGaps = std::max(maxGaps, 0);
@@ -239,18 +241,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timfillmiss(void *process)
-{
-  ModuleTimfillmiss timfillmiss;
-  timfillmiss.init(process);
-  timfillmiss.run();
-  timfillmiss.close();
-
-  return nullptr;
-}
diff --git a/src/Timpctl.cc b/src/Timpctl.cc
index 9ffa918b8e1961c0c368599fa8caa746da2669fb..3d0d4d5c6d3c813da67f4ad2a3ac3f1de627bf13 100644
--- a/src/Timpctl.cc
+++ b/src/Timpctl.cc
@@ -28,8 +28,23 @@
 #include "datetime.h"
 #include "field_functions.h"
 
-class ModuleTimpctl
+class Timpctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timpctl",
+    .operators = { { "timpctl", FieldFunc_Pctl, CMP_DATE, TimpctlHelp },
+                   { "yearpctl", FieldFunc_Pctl, CMP_YEAR, YearpctlHelp },
+                   { "monpctl", FieldFunc_Pctl, CMP_MONTH, MonpctlHelp },
+                   { "daypctl", FieldFunc_Pctl, CMP_DAY, DaypctlHelp },
+                   { "hourpctl", FieldFunc_Pctl, CMP_HOUR, HourpctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timpctl> registration = RegisterEntry<Timpctl>(module);
   CdiDateTime vDateTime0{};
 
   CdoStreamID streamID1;
@@ -58,16 +73,10 @@ class ModuleTimpctl
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    cdo_operator_add("timpctl",  FieldFunc_Pctl, CMP_DATE,  nullptr);
-    cdo_operator_add("yearpctl", FieldFunc_Pctl, CMP_YEAR,  nullptr);
-    cdo_operator_add("monpctl",  FieldFunc_Pctl, CMP_MONTH, nullptr);
-    cdo_operator_add("daypctl",  FieldFunc_Pctl, CMP_DAY,   nullptr);
-    cdo_operator_add("hourpctl", FieldFunc_Pctl, CMP_HOUR,  nullptr);
     // clang-format on
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
@@ -224,17 +233,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Timpctl(void *process)
-{
-  ModuleTimpctl timpctl;
-  timpctl.init(process);
-  timpctl.run();
-  timpctl.close();
-
-  return nullptr;
-}
diff --git a/src/Timselpctl.cc b/src/Timselpctl.cc
index 8351e487e5d402236fad65315240613083e10868..25a73642495b8a199d375135f6f617d55ba95633 100644
--- a/src/Timselpctl.cc
+++ b/src/Timselpctl.cc
@@ -24,8 +24,19 @@
 #include "datetime.h"
 #include "field_functions.h"
 
-class ModuleTimselpctl
+class Timselpctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timselpctl",
+    .operators = { { "timselpctl", FieldFunc_Pctl, 0, TimselpctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timselpctl> registration = RegisterEntry<Timselpctl>(module);
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -54,11 +65,8 @@ class ModuleTimselpctl
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("timselpctl", FieldFunc_Pctl, 0, nullptr);
 
     operator_input_arg("percentile number, numSets <,noffset <,nskip>>");
 
@@ -242,18 +250,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timselpctl(void *process)
-{
-  ModuleTimselpctl timselpctl;
-  timselpctl.init(process);
-  timselpctl.run();
-  timselpctl.close();
-
-  return nullptr;
-}
diff --git a/src/Timselstat.cc b/src/Timselstat.cc
index 02d4beaf6bbf1a6e17010332e2c7135ab79a308b..cf47fd3cae91b0213aa502b6d62714ad3aafb459 100644
--- a/src/Timselstat.cc
+++ b/src/Timselstat.cc
@@ -28,25 +28,28 @@
 #include "datetime.h"
 #include "field_functions.h"
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("timselrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("timselmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("timselmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("timselsum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("timselmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("timselavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("timselvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("timselvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("timselstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("timselstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-
-class ModuleTimselstat
+class Timselstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timselstat",
+    .operators = { { "timselrange", FieldFunc_Range, 0, TimselstatHelp },
+                   { "timselmin", FieldFunc_Min, 0, TimselstatHelp },
+                   { "timselmax", FieldFunc_Max, 0, TimselstatHelp },
+                   { "timselsum", FieldFunc_Sum, 0, TimselstatHelp },
+                   { "timselmean", FieldFunc_Mean, 0, TimselstatHelp },
+                   { "timselavg", FieldFunc_Avg, 0, TimselstatHelp },
+                   { "timselvar", FieldFunc_Var, 0, TimselstatHelp },
+                   { "timselvar1", FieldFunc_Var1, 0, TimselstatHelp },
+                   { "timselstd", FieldFunc_Std, 0, TimselstatHelp },
+                   { "timselstd1", FieldFunc_Std1, 0, TimselstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timselstat> registration = RegisterEntry<Timselstat>(module);
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -75,11 +78,8 @@ class ModuleTimselstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -186,11 +186,11 @@ public:
                     cdo_read_record(streamID1, rvars1);
                     if (lrange)
                       {
-                        vars2[varID][levelID].nmiss = rvars1.nmiss;
+                        vars2[varID][levelID].numMissVals = rvars1.numMissVals;
                         vars2[varID][levelID].vec_d = rvars1.vec_d;
                       }
 
-                    if (rvars1.nmiss || !rsamp1.empty())
+                    if (rvars1.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                         field2_vinit(rsamp1, rvars1);
@@ -201,7 +201,7 @@ public:
                     field.init(varList[varID]);
                     cdo_read_record(streamID1, field);
 
-                    if (field.nmiss || !rsamp1.empty())
+                    if (field.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                         field2_vincr(rsamp1, field);
@@ -287,19 +287,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timselstat(void *process)
-{
-  ModuleTimselstat timselstat;
-
-  timselstat.init(process);
-  timselstat.run();
-  timselstat.close();
-
-  return nullptr;
-}
diff --git a/src/Timsort.cc b/src/Timsort.cc
index 2dd174fa06d07d5b96c31e84423417234447ea3d..58af4eaba4ee971d6aeca1e79fa3f8516236324c 100644
--- a/src/Timsort.cc
+++ b/src/Timsort.cc
@@ -11,8 +11,6 @@
      Timsort    timsort         Sort over the time
 */
 
-#include <algorithm>  // sort
-
 #include <cdi.h>
 
 #include "process_int.h"
@@ -21,8 +19,20 @@
 #include "cimdOmp.h"
 #include "field_functions.h"
 
-class ModuleTimsort
+class Timsort : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timsort",
+    .operators = { { "timsort", TimsortHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timsort> registration = RegisterEntry<Timsort>(module);
+
   int varID, levelID;
   int nalloc = 0;
 
@@ -42,9 +52,8 @@ class ModuleTimsort
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -64,6 +73,7 @@ public:
 
     nvars = vlistNvars(vlistID1);
   }
+
   void
   run()
   {
@@ -123,7 +133,7 @@ public:
                     v.resize(nts);
                     for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_f[i];
 
-                    std::sort(v.begin(), v.end());
+                    ranges::sort(v);
 
                     for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_f[i] = v[t];
                   }
@@ -133,7 +143,7 @@ public:
                     v.resize(nts);
                     for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_d[i];
 
-                    std::sort(v.begin(), v.end());
+                    ranges::sort(v);
 
                     for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_d[i] = v[t];
                   }
@@ -161,24 +171,11 @@ public:
           }
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timsort(void *process)
-{
-  ModuleTimsort timsort;
-
-  timsort.init(process);
-  timsort.run();
-  timsort.close();
-
-  return nullptr;
-}
diff --git a/src/Timstat.cc b/src/Timstat.cc
index 69855976d179d14323275215a9f830f8193e7599..4e920c585659f25334130a197167543cf90242d6 100644
--- a/src/Timstat.cc
+++ b/src/Timstat.cc
@@ -104,72 +104,78 @@ set_missval(Field &field, const Field &samp, int numSets, double vfrac)
         }
     }
 
-  if (irun) field.nmiss = field_num_miss(field);
+  if (irun) field.numMissVals = field_num_miss(field);
 }
 
-static void
-addOperators(void)
+class Timstat : public Process
 {
-  // clang-format off
-  cdo_operator_add("timrange",   FieldFunc_Range,  CMP_DATE, nullptr);
-  cdo_operator_add("timmin",     FieldFunc_Min,    CMP_DATE, nullptr);
-  cdo_operator_add("timmax",     FieldFunc_Max,    CMP_DATE, nullptr);
-  cdo_operator_add("timminidx",  FieldFunc_Minidx, CMP_DATE, nullptr);
-  cdo_operator_add("timmaxidx",  FieldFunc_Maxidx, CMP_DATE, nullptr);
-  cdo_operator_add("timsum",     FieldFunc_Sum,    CMP_DATE, nullptr);
-  cdo_operator_add("timmean",    FieldFunc_Mean,   CMP_DATE, nullptr);
-  cdo_operator_add("timavg",     FieldFunc_Avg,    CMP_DATE, nullptr);
-  cdo_operator_add("timvar",     FieldFunc_Var,    CMP_DATE, nullptr);
-  cdo_operator_add("timvar1",    FieldFunc_Var1,   CMP_DATE, nullptr);
-  cdo_operator_add("timstd",     FieldFunc_Std,    CMP_DATE, nullptr);
-  cdo_operator_add("timstd1",    FieldFunc_Std1,   CMP_DATE, nullptr);
-  cdo_operator_add("yearrange",  FieldFunc_Range,  CMP_YEAR, nullptr);
-  cdo_operator_add("yearmin",    FieldFunc_Min,    CMP_YEAR, nullptr);
-  cdo_operator_add("yearmax",    FieldFunc_Max,    CMP_YEAR, nullptr);
-  cdo_operator_add("yearminidx", FieldFunc_Minidx, CMP_YEAR, nullptr);
-  cdo_operator_add("yearmaxidx", FieldFunc_Maxidx, CMP_YEAR, nullptr);
-  cdo_operator_add("yearsum",    FieldFunc_Sum,    CMP_YEAR, nullptr);
-  cdo_operator_add("yearmean",   FieldFunc_Mean,   CMP_YEAR, nullptr);
-  cdo_operator_add("yearavg",    FieldFunc_Avg,    CMP_YEAR, nullptr);
-  cdo_operator_add("yearvar",    FieldFunc_Var,    CMP_YEAR, nullptr);
-  cdo_operator_add("yearvar1",   FieldFunc_Var1,   CMP_YEAR, nullptr);
-  cdo_operator_add("yearstd",    FieldFunc_Std,    CMP_YEAR, nullptr);
-  cdo_operator_add("yearstd1",   FieldFunc_Std1,   CMP_YEAR, nullptr);
-  cdo_operator_add("monrange",   FieldFunc_Range,  CMP_MONTH, nullptr);
-  cdo_operator_add("monmin",     FieldFunc_Min,    CMP_MONTH, nullptr);
-  cdo_operator_add("monmax",     FieldFunc_Max,    CMP_MONTH, nullptr);
-  cdo_operator_add("monsum",     FieldFunc_Sum,    CMP_MONTH, nullptr);
-  cdo_operator_add("monmean",    FieldFunc_Mean,   CMP_MONTH, nullptr);
-  cdo_operator_add("monavg",     FieldFunc_Avg,    CMP_MONTH, nullptr);
-  cdo_operator_add("monvar",     FieldFunc_Var,    CMP_MONTH, nullptr);
-  cdo_operator_add("monvar1",    FieldFunc_Var1,   CMP_MONTH, nullptr);
-  cdo_operator_add("monstd",     FieldFunc_Std,    CMP_MONTH, nullptr);
-  cdo_operator_add("monstd1",    FieldFunc_Std1,   CMP_MONTH, nullptr);
-  cdo_operator_add("dayrange",   FieldFunc_Range,  CMP_DAY, nullptr);
-  cdo_operator_add("daymin",     FieldFunc_Min,    CMP_DAY, nullptr);
-  cdo_operator_add("daymax",     FieldFunc_Max,    CMP_DAY, nullptr);
-  cdo_operator_add("daysum",     FieldFunc_Sum,    CMP_DAY, nullptr);
-  cdo_operator_add("daymean",    FieldFunc_Mean,   CMP_DAY, nullptr);
-  cdo_operator_add("dayavg",     FieldFunc_Avg,    CMP_DAY, nullptr);
-  cdo_operator_add("dayvar",     FieldFunc_Var,    CMP_DAY, nullptr);
-  cdo_operator_add("dayvar1",    FieldFunc_Var1,   CMP_DAY, nullptr);
-  cdo_operator_add("daystd",     FieldFunc_Std,    CMP_DAY, nullptr);
-  cdo_operator_add("daystd1",    FieldFunc_Std1,   CMP_DAY, nullptr);
-  cdo_operator_add("hourrange",  FieldFunc_Range,  CMP_HOUR, nullptr);
-  cdo_operator_add("hourmin",    FieldFunc_Min,    CMP_HOUR, nullptr);
-  cdo_operator_add("hourmax",    FieldFunc_Max,    CMP_HOUR, nullptr);
-  cdo_operator_add("hoursum",    FieldFunc_Sum,    CMP_HOUR, nullptr);
-  cdo_operator_add("hourmean",   FieldFunc_Mean,   CMP_HOUR, nullptr);
-  cdo_operator_add("houravg",    FieldFunc_Avg,    CMP_HOUR, nullptr);
-  cdo_operator_add("hourvar",    FieldFunc_Var,    CMP_HOUR, nullptr);
-  cdo_operator_add("hourvar1",   FieldFunc_Var1,   CMP_HOUR, nullptr);
-  cdo_operator_add("hourstd",    FieldFunc_Std,    CMP_HOUR, nullptr);
-  cdo_operator_add("hourstd1",   FieldFunc_Std1,   CMP_HOUR, nullptr);
-  // clang-format on
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timstat",
+    // clang-format off
+    .operators = { { "timrange", FieldFunc_Range, CMP_DATE, TimstatHelp },
+                   { "timmin", FieldFunc_Min, CMP_DATE, TimstatHelp },
+                   { "timmax", FieldFunc_Max, CMP_DATE, TimstatHelp },
+                   { "timsum", FieldFunc_Sum, CMP_DATE, TimstatHelp },
+                   { "timmean", FieldFunc_Mean, CMP_DATE, TimstatHelp },
+                   { "timavg", FieldFunc_Avg, CMP_DATE, TimstatHelp },
+                   { "timvar", FieldFunc_Var, CMP_DATE, TimstatHelp },
+                   { "timvar1", FieldFunc_Var1, CMP_DATE, TimstatHelp },
+                   { "timstd", FieldFunc_Std, CMP_DATE, TimstatHelp },
+                   { "timstd1", FieldFunc_Std1, CMP_DATE, TimstatHelp },
+                   { "timminidx", FieldFunc_Minidx, CMP_DATE, TimstatHelp },
+                   { "timmaxidx", FieldFunc_Maxidx, CMP_DATE, TimstatHelp },
+                   { "yearrange", FieldFunc_Range, CMP_YEAR, YearstatHelp },
+                   { "yearmin", FieldFunc_Min, CMP_YEAR, YearstatHelp },
+                   { "yearmax", FieldFunc_Max, CMP_YEAR, YearstatHelp },
+                   { "yearsum", FieldFunc_Sum, CMP_YEAR, YearstatHelp },
+                   { "yearmean", FieldFunc_Mean, CMP_YEAR, YearstatHelp },
+                   { "yearavg", FieldFunc_Avg, CMP_YEAR, YearstatHelp },
+                   { "yearvar", FieldFunc_Var, CMP_YEAR, YearstatHelp },
+                   { "yearvar1", FieldFunc_Var1, CMP_YEAR, YearstatHelp },
+                   { "yearstd", FieldFunc_Std, CMP_YEAR, YearstatHelp },
+                   { "yearstd1", FieldFunc_Std1, CMP_YEAR, YearstatHelp },
+                   { "yearminidx", FieldFunc_Minidx, CMP_YEAR, YearstatHelp },
+                   { "yearmaxidx", FieldFunc_Maxidx, CMP_YEAR, YearstatHelp },
+                   { "monrange", FieldFunc_Range, CMP_MONTH, MonstatHelp },
+                   { "monmin", FieldFunc_Min, CMP_MONTH, MonstatHelp },
+                   { "monmax", FieldFunc_Max, CMP_MONTH, MonstatHelp },
+                   { "monsum", FieldFunc_Sum, CMP_MONTH, MonstatHelp },
+                   { "monmean", FieldFunc_Mean, CMP_MONTH, MonstatHelp },
+                   { "monavg", FieldFunc_Avg, CMP_MONTH, MonstatHelp },
+                   { "monvar", FieldFunc_Var, CMP_MONTH, MonstatHelp },
+                   { "monvar1", FieldFunc_Var1, CMP_MONTH, MonstatHelp },
+                   { "monstd", FieldFunc_Std, CMP_MONTH, MonstatHelp },
+                   { "monstd1", FieldFunc_Std1, CMP_MONTH, MonstatHelp },
+                   { "dayrange", FieldFunc_Range, CMP_DAY, DaystatHelp },
+                   { "daymin", FieldFunc_Min, CMP_DAY, DaystatHelp },
+                   { "daymax", FieldFunc_Max, CMP_DAY, DaystatHelp },
+                   { "daysum", FieldFunc_Sum, CMP_DAY, DaystatHelp },
+                   { "daymean", FieldFunc_Mean, CMP_DAY, DaystatHelp },
+                   { "dayavg", FieldFunc_Avg, CMP_DAY, DaystatHelp },
+                   { "dayvar", FieldFunc_Var, CMP_DAY, DaystatHelp },
+                   { "dayvar1", FieldFunc_Var1, CMP_DAY, DaystatHelp },
+                   { "daystd", FieldFunc_Std, CMP_DAY, DaystatHelp },
+                   { "daystd1", FieldFunc_Std1, CMP_DAY, DaystatHelp },
+                   { "hourrange", FieldFunc_Range, CMP_HOUR, HourstatHelp },
+                   { "hourmin", FieldFunc_Min, CMP_HOUR, HourstatHelp },
+                   { "hourmax", FieldFunc_Max, CMP_HOUR, HourstatHelp },
+                   { "hoursum", FieldFunc_Sum, CMP_HOUR, HourstatHelp },
+                   { "hourmean", FieldFunc_Mean, CMP_HOUR, HourstatHelp },
+                   { "houravg", FieldFunc_Avg, CMP_HOUR, HourstatHelp },
+                   { "hourvar", FieldFunc_Var, CMP_HOUR, HourstatHelp },
+                   { "hourvar1", FieldFunc_Var1, CMP_HOUR, HourstatHelp },
+                   { "hourstd", FieldFunc_Std, CMP_HOUR, HourstatHelp },
+                   { "hourstd1", FieldFunc_Std1, CMP_HOUR, HourstatHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timstat> registration = RegisterEntry<Timstat>(module);
 
-class ModuleTimstat
-{
   static const TimeStat timestatDate{ TimeStat::MEAN };
   CdiDateTime vDateTime0{};
   CdiDateTime vDateTimeN{};
@@ -181,7 +187,7 @@ class ModuleTimstat
   int taxisID2;
 
   CdoStreamID streamID3;
-  int vlistID3, taxisID3 = -1;
+  int vlistID3, taxisID3{ -1 };
 
   int ntsteps1;
 
@@ -198,8 +204,8 @@ class ModuleTimstat
   int compareDate;
   int maxrecs;
 
-  bool lvfrac = false;
-  double vfrac = 1.0;
+  bool lvfrac{ false };
+  double vfrac{ 1.0 };
 
   DateTimeList dtlist;
   std::vector<RecordInfo> recList;
@@ -211,11 +217,8 @@ class ModuleTimstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -244,10 +247,7 @@ public:
         else if (oargc > 1)
           cdo_abort("Too many arguments!");
       }
-    else
-      {
-        operator_check_argc(0);
-      }
+    else { operator_check_argc(0); }
 
     streamID1 = cdo_open_read(0);
 
@@ -370,13 +370,13 @@ public:
                     cdo_read_record(streamID1, rvars1);
                     if (lrange || lminidx || lmaxidx)
                       {
-                        vars2[varID][levelID].nmiss = rvars1.nmiss;
+                        vars2[varID][levelID].numMissVals = rvars1.numMissVals;
                         vars2[varID][levelID].vec_d = rvars1.vec_d;
                       }
 
                     if (lminidx || lmaxidx) field_fill(rvars1, 0.0);
 
-                    if (rvars1.nmiss || !rsamp1.empty())
+                    if (rvars1.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                         field2_vinit(rsamp1, rvars1);
@@ -387,7 +387,7 @@ public:
                     field.init(varList[varID]);
                     cdo_read_record(streamID1, field);
 
-                    if (field.nmiss || !rsamp1.empty())
+                    if (field.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                         field2_vincr(rsamp1, field);
@@ -441,10 +441,7 @@ public:
                 else
                   fieldc_stdvar_func(rvars1, vars2[varID][levelID], numSets, divisor);
               }
-            else if (lrange)
-              {
-                field2_sub(rvars1, vars2[varID][levelID]);
-              }
+            else if (lrange) { field2_sub(rvars1, vars2[varID][levelID]); }
           }
 
         if (Options::cdoVerbose) cdo_print("%s  vfrac = %g, numSets = %d", datetime_to_string(vDateTimeN), vfrac, numSets);
@@ -486,7 +483,7 @@ public:
                 if (!rsamp1.empty())
                   samp = rsamp1.vec_d;
                 else
-                  varray_fill(samp, (double) numSets);
+                  ranges::fill(samp, (double) numSets);
 
                 cdo_def_record(streamID3, varID, levelID);
                 cdo_write_record(streamID3, samp.data(), 0);
@@ -506,17 +503,5 @@ public:
     if (Options::cdoDiag) cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Timstat(void *process)
-{
-  ModuleTimstat timstat;
-  timstat.init(process);
-  timstat.run();
-  timstat.close();
-
-  return nullptr;
-}
diff --git a/src/Timstat2.cc b/src/Timstat2.cc
index 45f1a10525c5d0aa5cf77ab3b90535eb8a050314..b64fce1557d40b9ba906c57de43f51b4898331db 100644
--- a/src/Timstat2.cc
+++ b/src/Timstat2.cc
@@ -19,6 +19,15 @@
 #include "cimdOmp.h"
 #include "field_functions.h"
 
+double
+calc_pvalue(double cor, size_t n)
+{
+  // Author: Estanislao Gavilan
+  double t_stat = cor * std::sqrt((n - 2) / (1 - cor * cor));
+  double pvalue = 0.5 * (1.0 + std::erf(std::fabs(t_stat / std::sqrt(2.0))));
+  return pvalue;
+}
+
 // correlation in time
 template <typename T1, typename T2>
 void
@@ -55,17 +64,14 @@ correlation_init(bool hasMissValues, size_t gridsize, const Varray<T1> &x, const
 #pragma omp parallel for default(shared) schedule(static)
 #endif
 #endif
-      for (size_t i = 0; i < gridsize; ++i)
-        {
-          correlation_sum(i);
-        }
+      for (size_t i = 0; i < gridsize; ++i) { correlation_sum(i); }
     }
 }
 
 static void
 correlation_init(size_t gridsize, const Field &field1, const Field &field2, Varray<size_t> &nofvals, Varray2D<double> &work)
 {
-  auto hasMissValues = (field1.nmiss > 0 || field2.nmiss > 0);
+  auto hasMissValues = (field1.numMissVals > 0 || field2.numMissVals > 0);
 
   if (field1.memType == MemType::Float && field2.memType == MemType::Float)
     correlation_init(hasMissValues, gridsize, field1.vec_f, field2.vec_f, (float) field1.missval, (float) field2.missval, nofvals,
@@ -85,10 +91,11 @@ correlation(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varr
   auto missval1 = missval;
   auto missval2 = missval;
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < gridsize; ++i)
     {
       double cor;
+      double pvalue;
       auto nvals = nofvals[i];
       if (nvals > 0)
         {
@@ -104,18 +111,22 @@ correlation(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varr
           cor = DIVM(temp1, SQRTM(temp6));
           cor = std::clamp(cor, -1.0, 1.0);
 
-          if (dbl_is_equal(cor, missval)) nmiss++;
+          if (dbl_is_equal(cor, missval)) numMissVals++;
+
+          pvalue = (nvals <= 2) ? missval : ((fabs(cor) < 1) ? calc_pvalue(cor, nvals) : 1);
         }
       else
         {
-          nmiss++;
+          numMissVals++;
           cor = missval;
+          pvalue = missval;
         }
 
       work[0][i] = cor;
+      work[1][i] = pvalue;
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 // covariance in time
@@ -152,17 +163,14 @@ covariance_init(bool hasMissValues, size_t gridsize, const Varray<T1> &x, const
 #pragma omp parallel for default(shared) schedule(static)
 #endif
 #endif
-      for (size_t i = 0; i < gridsize; ++i)
-        {
-          covariance_sum(i);
-        }
+      for (size_t i = 0; i < gridsize; ++i) { covariance_sum(i); }
     }
 }
 
 static void
 covariance_init(size_t gridsize, const Field &field1, const Field &field2, Varray<size_t> &nofvals, Varray2D<double> &work)
 {
-  auto hasMissValues = (field1.nmiss > 0 || field2.nmiss > 0);
+  auto hasMissValues = (field1.numMissVals > 0 || field2.numMissVals > 0);
 
   if (field1.memType == MemType::Float && field2.memType == MemType::Float)
     covariance_init(hasMissValues, gridsize, field1.vec_f, field2.vec_f, (float) field1.missval, (float) field2.missval, nofvals,
@@ -182,7 +190,7 @@ covariance(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varra
   auto missval1 = missval;
   auto missval2 = missval;
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < gridsize; ++i)
     {
       double covar;
@@ -192,18 +200,18 @@ covariance(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varra
           double dnvals = nvals;
           auto temp = DIVM(MULM(work[0][i], work[1][i]), dnvals * dnvals);
           covar = SUBM(DIVM(work[2][i], dnvals), temp);
-          if (dbl_is_equal(covar, missval)) nmiss++;
+          if (dbl_is_equal(covar, missval)) numMissVals++;
         }
       else
         {
-          nmiss++;
+          numMissVals++;
           covar = missval;
         }
 
       work[0][i] = covar;
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 // rms in time
@@ -239,51 +247,58 @@ rmsd_init(size_t gridsize, const Field &field1, const Field &field2, Varray<size
 static size_t
 rmsd_compute(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varray<double> &rmsd)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < gridsize; ++i)
     {
       if (nofvals[i] > 0) { rmsd[i] = std::sqrt(rmsd[i] / (double) nofvals[i]); }
       else
         {
-          nmiss++;
+          numMissVals++;
           rmsd[i] = missval;
         }
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
-class ModuleTimstat2
+class Timstat2 : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timstat2",
+    .operators = { { "timcor", FieldFunc_Cor, 5, TimcorHelp },
+                   { "timcovar", FieldFunc_Covar, 3, TimcovarHelp },
+                   { "timrmsd", FieldFunc_Rmsd, 1, nullptr} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timstat2> registration = RegisterEntry<Timstat2>(module);
+
   int numWork{};
 
   CdiDateTime vDateTime{};
 
   CdoStreamID streamID1;
-  int taxisID1;
   CdoStreamID streamID2;
-
   CdoStreamID streamID3;
+  int taxisID1;
   int taxisID3;
 
-  int operfunc;
+  int operfunc{};
 
   VarList varList1, varList2;
 
   int nrecs1;
 
+  bool doWritePvalue{false};
+
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    cdo_operator_add("timcor",   FieldFunc_Cor,   5, nullptr);
-    cdo_operator_add("timcovar", FieldFunc_Covar, 3, nullptr);
-    cdo_operator_add("timrmsd",  FieldFunc_Rmsd,  1, nullptr);
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
     numWork = cdo_operator_f2(operatorID);
@@ -312,6 +327,17 @@ public:
     if (timeIsConst)
       for (int varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_CONSTANT);
 
+    auto const&var = varList1[0];
+    doWritePvalue = (operfunc == FieldFunc_Cor && varList1.size() == 1 && var.nlevels == 1);
+
+    if (doWritePvalue)
+      {
+        auto varID = vlistDefVar(vlistID3, var.gridID, var.zaxisID, var.timetype);
+        vlistDefVarName(vlistID3, varID, "pvalue");
+      }
+
+    vlistDefNtsteps(vlistID3, 1);
+
     vlistDefTaxis(vlistID3, taxisID3);
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
@@ -384,14 +410,8 @@ public:
             auto &rnofvals = nofvals[varID][levelID];
 
             if (operfunc == FieldFunc_Cor) { correlation_init(gridsize, field1, field2, rnofvals, rwork); }
-            else if (operfunc == FieldFunc_Covar)
-              {
-                covariance_init(gridsize, field1, field2, rnofvals, rwork);
-              }
-            else if (operfunc == FieldFunc_Rmsd)
-              {
-                rmsd_init(gridsize, field1, field2, rnofvals, rwork[0]);
-              }
+            else if (operfunc == FieldFunc_Covar) { covariance_init(gridsize, field1, field2, rnofvals, rwork); }
+            else if (operfunc == FieldFunc_Rmsd) { rmsd_init(gridsize, field1, field2, rnofvals, rwork[0]); }
           }
 
         tsID++;
@@ -412,19 +432,19 @@ public:
         auto &rwork = work[varID][levelID];
         const auto &rnofvals = nofvals[varID][levelID];
 
-        size_t nmiss = 0;
-        if (operfunc == FieldFunc_Cor) { nmiss = correlation(gridsize, missval, rnofvals, rwork); }
-        else if (operfunc == FieldFunc_Covar)
-          {
-            nmiss = covariance(gridsize, missval, rnofvals, rwork);
-          }
-        else if (operfunc == FieldFunc_Rmsd)
-          {
-            nmiss = rmsd_compute(gridsize, missval, rnofvals, rwork[0]);
-          }
+        size_t numMissVals = 0;
+        if (operfunc == FieldFunc_Cor) { numMissVals = correlation(gridsize, missval, rnofvals, rwork); }
+        else if (operfunc == FieldFunc_Covar) { numMissVals = covariance(gridsize, missval, rnofvals, rwork); }
+        else if (operfunc == FieldFunc_Rmsd) { numMissVals = rmsd_compute(gridsize, missval, rnofvals, rwork[0]); }
 
         cdo_def_record(streamID3, varID, levelID);
-        cdo_write_record(streamID3, rwork[0].data(), nmiss);
+        cdo_write_record(streamID3, rwork[0].data(), numMissVals);
+
+        if (doWritePvalue)
+          {
+            cdo_def_record(streamID3, 1, levelID);
+            cdo_write_record(streamID3, rwork[1].data(), numMissVals);
+          }
       }
   }
 
@@ -434,18 +454,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Timstat2(void *process)
-{
-  ModuleTimstat2 timstat2;
-  timstat2.init(process);
-  timstat2.run();
-  timstat2.close();
-
-  return nullptr;
-}
diff --git a/src/Timstat3.cc b/src/Timstat3.cc
index ed2a545efd4315533a5c49bb45258d0b92e01aa9..332514710ff7105fe67faae912e1129b380aa4ee 100644
--- a/src/Timstat3.cc
+++ b/src/Timstat3.cc
@@ -98,14 +98,26 @@ meandiff2test(double rconst, double risk, size_t gridsize, double missval, Varra
     for (size_t i = 0; i < gridsize; ++i) out[i] = meandiff2test_kernel(i, is_equal);
 }
 
-class ModuleTimstat3
+class Timstat3 : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Timstat3",
+    .operators = { { "meandiff2test"}, { "varquot2test"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Timstat3> registration = RegisterEntry<Timstat3>(module);
+
+  int VARQUOT2TEST, MEANDIFF2TEST;
   int vlistID[NIN], vlistID2 = -1;
 
   CdoStreamID streamID[NIN];
-  int taxisID1;
-
   CdoStreamID streamID3;
+  int taxisID1;
   int taxisID3;
 
   double rconst;
@@ -117,18 +129,12 @@ class ModuleTimstat3
 
   VarList varList0;
 
-  int VARQUOT2TEST, MEANDIFF2TEST;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    VARQUOT2TEST  = cdo_operator_add("varquot2test",  0, 0, nullptr);
-    MEANDIFF2TEST = cdo_operator_add("meandiff2test", 0, 0, nullptr);
-    // clang-format on
+    VARQUOT2TEST = module.get_id("varquot2test");
+    MEANDIFF2TEST = module.get_id("meandiff2test");
 
     operatorID = cdo_operator_id();
 
@@ -227,7 +233,7 @@ public:
 
                 if (tsID == 0 && is == 0) recList[recID].set(varID, levelID);
 
-                cdo_read_record(streamID[is], inField.vec_d.data(), &inField.nmiss);
+                cdo_read_record(streamID[is], inField.vec_d.data(), &inField.numMissVals);
 
                 auto const &inArray = inField.vec_d;
                 auto &rwork1 = work[varID][levelID][3 * is + 0];
@@ -262,10 +268,7 @@ public:
         auto const &rwork = work[varID][levelID];
 
         if (operatorID == VARQUOT2TEST) { varquot2test(rconst, risk, var.gridsize, var.missval, rwork, out[0].vec_d); }
-        else if (operatorID == MEANDIFF2TEST)
-          {
-            meandiff2test(rconst, risk, var.gridsize, var.missval, rwork, out[0].vec_d);
-          }
+        else if (operatorID == MEANDIFF2TEST) { meandiff2test(rconst, risk, var.gridsize, var.missval, rwork, out[0].vec_d); }
 
         out[0].missval = var.missval;
         cdo_def_record(streamID3, varID, levelID);
@@ -278,18 +281,5 @@ public:
   {
     cdo_stream_close(streamID3);
     for (int is = 0; is < NIN; ++is) cdo_stream_close(streamID[is]);
-
-    cdo_finish();
   }
 };
-
-void *
-Timstat3(void *process)
-{
-  ModuleTimstat3 timstat3;
-  timstat3.init(process);
-  timstat3.run();
-  timstat3.close();
-
-  return nullptr;
-}
diff --git a/src/Tinfo.cc b/src/Tinfo.cc
index 5075a42a5a0597a347087c25c49fdbbc051900ae..5994ca1f54185ae1061b10b5cc855c39ce8c66cd 100644
--- a/src/Tinfo.cc
+++ b/src/Tinfo.cc
@@ -49,8 +49,8 @@ print_bounds(int taxisID, int calendar)
 
 static int
 fill_gap(int ngaps, int (&ntsm)[MaxNTSM], int (&rangetsm)[MaxGaps][2], CdiDateTime (&vDateTimesM)[MaxGaps][MaxNTSM], int tsID,
-         TimeIncrement timeIncr0, CdiDateTime vDateTime, CdiDateTime vDateTime0, int calendar, int day0,
-         JulianDate julianDate, JulianDate julianDate0)
+         TimeIncrement timeIncr0, CdiDateTime vDateTime, CdiDateTime vDateTime0, int calendar, int day0, JulianDate julianDate,
+         JulianDate julianDate0)
 {
   int its = 0;
   int year, month, day;
@@ -108,8 +108,19 @@ fill_gap(int ngaps, int (&ntsm)[MaxNTSM], int (&rangetsm)[MaxGaps][2], CdiDateTi
   return its;
 }
 
-class ModuleTinfo
+class Tinfo : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Tinfo",
+    .operators = { { "tinfo"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Tinfo> registration = RegisterEntry<Tinfo>(module);
   CdoStreamID streamID;
   int taxisID;
   int ntsteps;
@@ -135,9 +146,8 @@ class ModuleTinfo
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -258,15 +268,15 @@ public:
                 jdelta0 = jdelta;
                 timeIncr0 = timeIncr;
 
-                its = fill_gap(ngaps, ntsm, rangetsm, vDateTimesM, 1, timeIncr0, vDateTimeFirst, vDateTime, calendar,
-                               day, julianDate0, julianDate_encode(calendar, vDateTimeFirst));
+                its = fill_gap(ngaps, ntsm, rangetsm, vDateTimesM, 1, timeIncr0, vDateTimeFirst, vDateTime, calendar, day,
+                               julianDate0, julianDate_encode(calendar, vDateTimeFirst));
 
                 arrow = '^';
               }
             else
               {
-                its = fill_gap(ngaps, ntsm, rangetsm, vDateTimesM, tsID, timeIncr0, vDateTime, vDateTime0, calendar,
-                               day0, julianDate, julianDate0);
+                its = fill_gap(ngaps, ntsm, rangetsm, vDateTimesM, tsID, timeIncr0, vDateTime, vDateTime0, calendar, day0,
+                               julianDate, julianDate0);
 
                 arrow = '<';
 
@@ -359,17 +369,5 @@ public:
             fprintf(stdout, "\n");
           }
       }
-
-    cdo_finish();
   }
 };
-
-void *
-Tinfo(void *process)
-{
-  ModuleTinfo tinfo;
-  tinfo.init(process);
-  tinfo.run();
-  tinfo.close();
-  return nullptr;
-}
diff --git a/src/Tocomplex.cc b/src/Tocomplex.cc
index 0cd399d8dbf059e7720352f5a7ed256b7ed7056e..958b8385979d30a07979d14c1aa8913a94213268 100644
--- a/src/Tocomplex.cc
+++ b/src/Tocomplex.cc
@@ -10,8 +10,20 @@
 #include "process_int.h"
 #include "cdo_default_values.h"  // Namespace CdoDefault
 
-class ModuleTocomplex
+class Tocomplex : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Tocomplex",
+    .operators = { { "retocomplex"}, { "imtocomplex"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Tocomplex> registration = RegisterEntry<Tocomplex>(module);
+  int RETOCOMPLEX, IMTOCOMPLEX;
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -24,24 +36,22 @@ class ModuleTocomplex
   VarList varList1;
   Varray<double> array1, array2;
 
-  int RETOCOMPLEX,IMTOCOMPLEX;
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
-     RETOCOMPLEX = cdo_operator_add("retocomplex", 0, 0, nullptr);
-     IMTOCOMPLEX = cdo_operator_add("imtocomplex", 0, 0, nullptr);
+    RETOCOMPLEX = module.get_id("retocomplex");
+    IMTOCOMPLEX = module.get_id("imtocomplex");
 
-     operatorID = cdo_operator_id();
+    operatorID = cdo_operator_id();
 
     operator_check_argc(0);
 
-     streamID1 = cdo_open_read(0);
+    streamID1 = cdo_open_read(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-     vlistID2 = vlistDuplicate(vlistID1);
+    vlistID2 = vlistDuplicate(vlistID1);
 
     auto nvars = vlistNvars(vlistID2);
     for (int varID = 0; varID < nvars; ++varID)
@@ -51,12 +61,12 @@ public:
         vlistDefVarDatatype(vlistID2, varID, datatype);
       }
 
-     taxisID1 = vlistInqTaxis(vlistID1);
-     taxisID2 = taxisDuplicate(taxisID1);
+    taxisID1 = vlistInqTaxis(vlistID1);
+    taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
     // if (CdoDefault::FileType != CDI_FILETYPE_EXT) cdo_abort("Complex numbers need EXTRA format; used CDO option -f ext!");
-     streamID2 = cdo_open_write(1);
+    streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
@@ -81,12 +91,12 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            size_t nmiss;
+            size_t numMissVals;
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto gridsize = varList1[varID].gridsize;
             if (operatorID == RETOCOMPLEX)
@@ -106,7 +116,7 @@ public:
                   }
               }
 
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
@@ -119,19 +129,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Tocomplex(void *process)
-{
-  ModuleTocomplex tocomplex;
-
-  tocomplex.init(process);
-  tocomplex.run();
-  tocomplex.close();
-
-  return nullptr;
-}
diff --git a/src/Transpose.cc b/src/Transpose.cc
index 6b8f827c29785017d6e65d679fc0689d300ecfcf..ef454755c28c53d86a7053a388c0cebea1018ffa 100644
--- a/src/Transpose.cc
+++ b/src/Transpose.cc
@@ -37,8 +37,20 @@ transxy(int gridID, const Varray<double> &v1, Varray<double> &v2)
     }
 }
 
-class ModuleTranspose
+class Transpose : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Transpose",
+    .operators = { { "transxy"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Transpose> registration = RegisterEntry<Transpose>(module);
+
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -53,10 +65,8 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -106,16 +116,16 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            size_t nmiss;
+            size_t numMissVals;
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, array1.data(), &nmiss);
+            cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto gridID = vlistInqVarGrid(vlistID1, varID);
             transxy(gridID, array1, array2);
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, array2.data(), nmiss);
+            cdo_write_record(streamID2, array2.data(), numMissVals);
           }
 
         tsID++;
@@ -127,17 +137,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Transpose(void *process)
-{
-  ModuleTranspose transpose;
-  transpose.init(process);
-  transpose.run();
-  transpose.close();
-  return nullptr;
-}
diff --git a/src/Trend.cc b/src/Trend.cc
index c8185b4429f0270d5c47a59c744ca1f4656b97bc..6cda549976a6b8f49c51eaeb0b38eb95580b5ecb 100644
--- a/src/Trend.cc
+++ b/src/Trend.cc
@@ -32,7 +32,7 @@ trendGetParameter(bool &tstepIsEqual)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -50,8 +50,19 @@ trendGetParameter(bool &tstepIsEqual)
     }
 }
 
-class ModuleTrend
+class Trend : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Trend",
+    .operators = { { "trend", TrendHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 2, OnlyFirst },
+  };
+  inline static RegisterEntry<Trend> registration = RegisterEntry<Trend>(module);
 
   static const int nwork = 5;
   FieldVector2D work[nwork];
@@ -76,10 +87,8 @@ class ModuleTrend
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     trendGetParameter(tstepIsEqual);
 
     streamID1 = cdo_open_read(0);
@@ -119,7 +128,7 @@ public:
 
     calendar = taxisInqCalendar(taxisID1);
   }
-  
+
   void
   run()
   {
@@ -147,7 +156,7 @@ public:
 
             recList[recID].set(varID, levelID);
 
-            cdo_read_record(streamID1, field1.vec_d.data(), &field1.nmiss);
+            cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
 
             auto gridsize = varList1[varID].gridsize;
             auto missval = varList1[varID].missval;
@@ -221,25 +230,12 @@ public:
         cdo_write_record(streamID3, field2.vec_d.data(), field_num_miss(field2));
       }
   }
-  
+
   void
   close()
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Trend(void *process)
-{
-  ModuleTrend trend;
-  trend.init(process);
-  trend.run();
-  trend.close();
-
-  return nullptr;
-}
diff --git a/src/Trendarith.cc b/src/Trendarith.cc
index a73d01ef341e3e8152695fa70060c21a8cff8ca9..56a5ea7d1cb4cb9a7500706390493e3ab45971ea 100644
--- a/src/Trendarith.cc
+++ b/src/Trendarith.cc
@@ -29,9 +29,7 @@ add_trend(double zj, const Varray<T> &v1, const Varray<double> &v2, const Varray
   auto missval1 = mv;
   auto missval2 = mv;
 
-  auto add_kernel = [&](auto i, auto is_EQ) {
-    return ADDM(v1[i], ADDM(v2[i], MULM(v3[i], zj)));
-  };
+  auto add_kernel = [&](auto i, auto is_EQ) { return ADDM(v1[i], ADDM(v2[i], MULM(v3[i], zj))); };
 
   if (std::isnan(missval1))
     for (size_t i = 0; i < n; ++i) v4[i] = add_kernel(i, dbl_is_equal);
@@ -57,9 +55,7 @@ sub_trend(double zj, const Varray<T> &v1, const Varray<double> &v2, const Varray
   auto missval1 = mv;
   auto missval2 = mv;
 
-  auto sub_kernel = [&](auto i, auto is_EQ) {
-    return SUBM(v1[i], ADDM(v2[i], MULM(v3[i], zj)));
-  };
+  auto sub_kernel = [&](auto i, auto is_EQ) { return SUBM(v1[i], ADDM(v2[i], MULM(v3[i], zj))); };
 
   if (std::isnan(missval1))
     for (size_t i = 0; i < n; ++i) v4[i] = sub_kernel(i, dbl_is_equal);
@@ -88,7 +84,7 @@ trendarithGetParameter(bool &tstepIsEqual)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -106,8 +102,23 @@ trendarithGetParameter(bool &tstepIsEqual)
     }
 }
 
-class ModuleTrendarith
+class Trendarith : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Trendarith",
+    // clang-format off
+    .operators = { { "addtrend", FieldFunc_Add, 0, TrendarithHelp },
+                   { "subtrend", FieldFunc_Sub, 0, TrendarithHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Trendarith> registration = RegisterEntry<Trendarith>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -130,13 +141,8 @@ class ModuleTrendarith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("addtrend", FieldFunc_Add, 0, nullptr);
-    cdo_operator_add("subtrend", FieldFunc_Sub, 0, nullptr);
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -175,7 +181,7 @@ public:
         {
           int varID, levelID;
           cdo_inq_record(streamID2, &varID, &levelID);
-          cdo_read_record(streamID2, vars2[varID][levelID].vec_d.data(), &vars2[varID][levelID].nmiss);
+          cdo_read_record(streamID2, vars2[varID][levelID].vec_d.data(), &vars2[varID][levelID].numMissVals);
         }
     }
 
@@ -185,7 +191,7 @@ public:
         {
           int varID, levelID;
           cdo_inq_record(streamID3, &varID, &levelID);
-          cdo_read_record(streamID3, vars3[varID][levelID].vec_d.data(), &vars3[varID][levelID].nmiss);
+          cdo_read_record(streamID3, vars3[varID][levelID].vec_d.data(), &vars3[varID][levelID].numMissVals);
         }
     }
 
@@ -226,7 +232,7 @@ public:
             else
               sub_trend(zj, field1, vars2[varID][levelID], vars3[varID][levelID], field4);
 
-            field4.nmiss = field1.nmiss;
+            field4.numMissVals = field1.numMissVals;
             cdo_def_record(streamID4, varID, levelID);
             cdo_write_record(streamID4, field4);
           }
@@ -242,18 +248,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Trendarith(void *process)
-{
-  ModuleTrendarith trendarith;
-  trendarith.init(process);
-  trendarith.run();
-  trendarith.close();
-
-  return nullptr;
-}
diff --git a/src/Tstepcount.cc b/src/Tstepcount.cc
index 0b61f8261a200016ce5770ce710559acc8f610cf..a8eb370dba52861c81f04391d06ab500864d1235 100644
--- a/src/Tstepcount.cc
+++ b/src/Tstepcount.cc
@@ -36,8 +36,20 @@ tstepcount(long nts, T missval, const Varray<T> &v, T refval)
   return (j == nts) ? missval : (T) n;
 }
 
-class ModuleTstepcount
+class Tstepcount : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Tstepcount",
+    .operators = { { "tstepcount"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Tstepcount> registration = RegisterEntry<Tstepcount>(module);
+
   CdiDateTime vDateTime{};
 
   CdoStreamID streamID1;
@@ -55,10 +67,8 @@ class ModuleTstepcount
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     refval = (cdo_operator_argc() == 1) ? parameter_to_double(cdo_operator_argv(0)) : 0.0;
 
     streamID1 = cdo_open_read(0);
@@ -80,13 +90,14 @@ public:
 
     varList_init(varList1, vlistID1);
   }
+  
   void
   run()
   {
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
         constexpr size_t NALLOC_INC = 1024;
@@ -124,7 +135,7 @@ public:
 #endif
             for (size_t i = 0; i < gridsize; ++i)
               {
-                const auto ompthID = cdo_omp_get_thread_num();
+                auto ompthID = cdo_omp_get_thread_num();
 
                 if (memType == MemType::Float)
                   {
@@ -132,7 +143,7 @@ public:
                     v.resize(nts);
                     for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_f[i];
 
-                    const auto count = tstepcount(nts, (float) missval, v, (float) refval);
+                    auto count = tstepcount(nts, (float) missval, v, (float) refval);
 
                     vars[0][varID][levelID].vec_f[i] = count;
                   }
@@ -142,7 +153,7 @@ public:
                     v.resize(nts);
                     for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_d[i];
 
-                    const auto count = tstepcount(nts, missval, v, refval);
+                    auto count = tstepcount(nts, missval, v, refval);
 
                     vars[0][varID][levelID].vec_d[i] = count;
                   }
@@ -164,24 +175,11 @@ public:
           }
       }
   }
+
   void
   close()
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Tstepcount(void *process)
-{
-  ModuleTstepcount tstepcount;
-
-  tstepcount.init(process);
-  tstepcount.run();
-  tstepcount.close();
-
-  return nullptr;
-}
diff --git a/src/Unpack.cc b/src/Unpack.cc
index a9c56afb9b116e63945a6766c66c3c19bacae6dd..46165ad0f770a12a29e113cd6640570a2e7c7b84 100644
--- a/src/Unpack.cc
+++ b/src/Unpack.cc
@@ -9,8 +9,20 @@
 
 #include "process_int.h"
 
-class ModuleUnpack
+class Unpack : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Unpack",
+    .operators = { { "unpack", UnpackHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Unpack> registration = RegisterEntry<Unpack>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -21,10 +33,8 @@ class ModuleUnpack
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -92,17 +102,5 @@ public:
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Unpack(void *process)
-{
-  ModuleUnpack unpack;
-  unpack.init(process);
-  unpack.run();
-  unpack.close();
-  return nullptr;
-}
diff --git a/src/Vargen.cc b/src/Vargen.cc
index 7e4895726842e11f63aee861f68b4b3ad650eef0..7b0d7d75ed964f9da4f0ea6b8b14b661688985d2 100644
--- a/src/Vargen.cc
+++ b/src/Vargen.cc
@@ -313,20 +313,32 @@ define_temperature_attributes(int vlistID, int varID)
   cdiDefKeyString(vlistID, varID, CDI_KEY_UNITS, "K");
 }
 
-class ModuleVargen
+class Vargen : public Process
 {
-private:
-  int RANDOM;
-  int SINCOS;
-  int COSHILL;
-  int TESTFIELD;
-  int CONST;
-  int SEQ;
-  int TOPO;
-  int TEMP;
-  int MASK;
-  int STDATM;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vargen",
+    .operators = { { "random", 0, 0, "grid description file or name, <seed>", VargenHelp },
+                   { "const", 0, 0, "constant value, grid description file or name", VargenHelp },
+                   { "sincos", 0, 0, "grid description file or name", VargenHelp },
+                   { "coshill", 0, 0, "grid description file or name", VargenHelp },
+                   { "testfield", 0, 0, "grid description file or name", VargenHelp },
+                   { "seq", 0, 0, "start,end,<increment>", VargenHelp },
+                   { "topo", VargenHelp },
+                   { "temp", VargenHelp },
+                   { "mask", VargenHelp },
+                   { "stdatm", 0, 0, "height levels[m]", VargenHelp } },
+    .aliases = { { "for", "seq" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vargen> registration = RegisterEntry<Vargen>(module);
+
+  int RANDOM, SINCOS, COSHILL, TESTFIELD, CONST, SEQ, TOPO, TEMP, MASK, STDATM;
 
+private:
   static constexpr size_t nlat = 360, nlon = 720;
   double lon[nlon], lat[nlat];
   int nlevels = 1;
@@ -349,23 +361,19 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-    RANDOM    = cdo_operator_add("random",    0, 0, "grid description file or name, <seed>");
-    SINCOS    = cdo_operator_add("sincos",    0, 0, "grid description file or name");
-    COSHILL   = cdo_operator_add("coshill",   0, 0, "grid description file or name");
-    //not usesd todo: make unavailable for non developers
-    TESTFIELD = cdo_operator_add("testfield", 0, 0, "grid description file or name");
-    CONST     = cdo_operator_add("const",     0, 0, "constant value, grid description file or name");
-    SEQ       = cdo_operator_add("seq",       0, 0, "start, end, <increment>");
-    TOPO      = cdo_operator_add("topo",      0, 0, nullptr);
-    TEMP      = cdo_operator_add("temp",      0, 0, nullptr);
-    MASK      = cdo_operator_add("mask",      0, 0, nullptr);
-    STDATM    = cdo_operator_add("stdatm",    0, 0, "height levels [m]");
-    // clang-format on
+    RANDOM = module.get_id("random");
+    SINCOS = module.get_id("sincos");
+    COSHILL = module.get_id("coshill");
+    // not usesd todo: make unavailable for non developers
+    TESTFIELD = module.get_id("testfield");
+    CONST = module.get_id("const");
+    SEQ = module.get_id("seq");
+    TOPO = module.get_id("topo");
+    TEMP = module.get_id("temp");
+    MASK = module.get_id("mask");
+    STDATM = module.get_id("stdatm");
 
     operatorID = cdo_operator_id();
 
@@ -555,17 +563,5 @@ public:
     cdo_stream_close(streamID);
 
     vlistDestroy(vlistID);
-
-    cdo_finish();
   }
 };
-
-void *
-Vargen(void *process)
-{
-  ModuleVargen vargen;
-  vargen.init(process);
-  vargen.run();
-  vargen.close();
-  return nullptr;
-}
diff --git a/src/Varrms.cc b/src/Varrms.cc
index 30131783f9b805eb84d83832c27782f4da4ad00c..8a6ed153c68d8eab726836a19665ffb77f09928b 100644
--- a/src/Varrms.cc
+++ b/src/Varrms.cc
@@ -32,7 +32,7 @@ var_rms(const Varray<double> &w, const FieldVector &field1, const FieldVector &f
   auto len = gridInqSize(grid1);
   if (len != gridInqSize(grid2)) cdo_abort("fields have different size!");
 
-  // if ( nmiss1 )
+  // if ( numMissVals1 )
   {
     for (size_t k = 0; k < nlev; ++k)
       {
@@ -58,15 +58,27 @@ else
 
   auto ravg = SQRTM(DIVM(rsum, rsumw));
 
-  size_t rnmiss = 0;
-  if (is_EQ(ravg, missval1)) rnmiss++;
+  size_t rnumMissVals = 0;
+  if (is_EQ(ravg, missval1)) rnumMissVals++;
 
   field3.vec_d[0] = ravg;
-  field3.nmiss = rnmiss;
+  field3.numMissVals = rnumMissVals;
 }
 
-class ModuleVarrms
+class Varrms : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Varrms",
+    .operators = { { "varrms"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Varrms> registration = RegisterEntry<Varrms>(module);
+
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -92,11 +104,9 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -163,15 +173,15 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            size_t nmiss;
+            size_t numMissVals;
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vars1[varID][levelID].vec_d.data(), &nmiss);
-            if (nmiss) cdo_abort("Missing values unsupported for this operator!");
+            cdo_read_record(streamID1, vars1[varID][levelID].vec_d.data(), &numMissVals);
+            if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
 
             cdo_inq_record(streamID2, &varID, &levelID);
-            cdo_read_record(streamID2, vars2[varID][levelID].vec_d.data(), &nmiss);
-            if (nmiss) cdo_abort("Missing values unsupported for this operator!");
+            cdo_read_record(streamID2, vars2[varID][levelID].vec_d.data(), &numMissVals);
+            if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
           }
 
         for (int varID = 0; varID < nvars; ++varID)
@@ -191,7 +201,7 @@ public:
             var_rms(weights, vars1[varID], vars2[varID], field3);
 
             cdo_def_record(streamID3, varID, 0);
-            cdo_write_record(streamID3, field3.vec_d.data(), field3.nmiss);
+            cdo_write_record(streamID3, field3.vec_d.data(), field3.numMissVals);
           }
 
         tsID++;
@@ -206,18 +216,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID3);
-
-    cdo_finish();
   }
 };
-
-void *
-Varrms(void *process)
-{
-  ModuleVarrms varrms;
-  varrms.init(process);
-  varrms.run();
-  varrms.close();
-
-  return nullptr;
-}
diff --git a/src/Varsstat.cc b/src/Varsstat.cc
index 5450813224250a3998aca0aacad148b1388968bb..21d525bae67b84aae87776ce9ffb918894fb473c 100644
--- a/src/Varsstat.cc
+++ b/src/Varsstat.cc
@@ -63,25 +63,28 @@ set_attributes(const VarList &varList1, int vlistID2, int varID2, int operatorID
     }
 }
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("varsrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("varsmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("varsmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("varssum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("varsmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("varsavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("varsvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("varsvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("varsstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("varsstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-
-class ModuleVarsstat
+class Varsstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Varsstat",
+    .operators = { { "varsrange", FieldFunc_Range, 0, VarsstatHelp },
+                   { "varsmin", FieldFunc_Min, 0, VarsstatHelp },
+                   { "varsmax", FieldFunc_Max, 0, VarsstatHelp },
+                   { "varssum", FieldFunc_Sum, 0, VarsstatHelp },
+                   { "varsmean", FieldFunc_Mean, 0, VarsstatHelp },
+                   { "varsavg", FieldFunc_Avg, 0, VarsstatHelp },
+                   { "varsstd", FieldFunc_Std, 0, VarsstatHelp },
+                   { "varsstd1", FieldFunc_Std1, 0, VarsstatHelp },
+                   { "varsvar", FieldFunc_Var, 0, VarsstatHelp },
+                   { "varsvar1", FieldFunc_Var1, 0, VarsstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Varsstat> registration = RegisterEntry<Varsstat>(module);
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -105,11 +108,8 @@ class ModuleVarsstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -212,13 +212,13 @@ public:
                 cdo_read_record(streamID1, rvars1);
                 if (lrange)
                   {
-                    vars2[levelID].nmiss = rvars1.nmiss;
+                    vars2[levelID].numMissVals = rvars1.numMissVals;
                     vars2[levelID].vec_d = rvars1.vec_d;
                   }
 
                 if (lvarstd) field2_moq(vars2[levelID], rvars1);
 
-                if (rvars1.nmiss || !rsamp1.empty())
+                if (rvars1.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                     field2_vinit(rsamp1, rvars1);
@@ -229,7 +229,7 @@ public:
                 field.init(varList1[varID]);
                 cdo_read_record(streamID1, field);
 
-                if (field.nmiss || !rsamp1.empty())
+                if (field.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size, rvars1.nsamp);
                     field2_vincr(rsamp1, field);
@@ -283,18 +283,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Varsstat(void *process)
-{
-  ModuleVarsstat varsstat;
-  varsstat.init(process);
-  varsstat.run();
-  varsstat.close();
-
-  return nullptr;
-}
diff --git a/src/Verifygrid.cc b/src/Verifygrid.cc
index 30b6dde568346092ef392d5d671be38cbbb09e76..8c8ca7a7cc4f273db8d97fd36b622606c9efe43d 100644
--- a/src/Verifygrid.cc
+++ b/src/Verifygrid.cc
@@ -771,8 +771,19 @@ print_lonlat(int dig, int gridID, size_t gridsize, const Varray<double> &lon, co
   if (ymm.min < -90.00001 || ymm.max > 90.00001) cdo_warning("Grid cell center latitudes out of range!");
 }
 
-class ModuleVerifygrid
+class Verifygrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Verifygrid",
+    .operators = { { "verifygrid", VerifygridHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Verifygrid> registration = RegisterEntry<Verifygrid>(module);
 
   CdoStreamID streamID;
   int vlistID;
@@ -781,11 +792,8 @@ class ModuleVerifygrid
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("verifygrid", 0, 0, nullptr);
 
     operator_check_argc(0);
 
@@ -881,18 +889,5 @@ public:
   close()
   {
     cdo_stream_close(streamID);
-
-    cdo_finish();
   }
 };
-
-void *
-Verifygrid(void *process)
-{
-
-  ModuleVerifygrid verifygrid;
-  verifygrid.init(process);
-  verifygrid.run();
-  verifygrid.close();
-  return nullptr;
-}
diff --git a/src/Verifyweights.cc b/src/Verifyweights.cc
index d59e2980d7b97a6a6f308e1b800948ba3e09891c..910cea4664589a5f1bcb3458059a7557cadee05c 100644
--- a/src/Verifyweights.cc
+++ b/src/Verifyweights.cc
@@ -165,11 +165,11 @@ struct RemapVarsW
   RemapMethod mapType{ RemapMethod::UNDEF };  // identifier for remapping method
   NormOpt normOpt{ NormOpt::NONE };           // option for normalization (conserv only)
   size_t numLinks = 0;                        // number of links for remapping
-  size_t numWeights = 0;                         // number of weights used in remapping
+  size_t numWeights = 0;                      // number of weights used in remapping
 
   Varray<size_t> srcCellIndices;  // source grid indices for each link
   Varray<size_t> tgtCellIndices;  // target grid indices for each link
-  Varray<double> weights;             // map weights for each link [numLinks*numWeights]
+  Varray<double> weights;         // map weights for each link [numLinks*numWeights]
 };
 
 static void
@@ -374,10 +374,7 @@ cdfWriteVarSize(int ncfileid, int ncvarid, nc_type sizetype, size_t len, size_t
       nce(nc_put_var_int(ncfileid, ncvarid, iarray.data()));
     }
 #ifdef HAVE_NETCDF4
-  else
-    {
-      nce(nc_put_var_ulonglong(ncfileid, ncvarid, (unsigned long long *) array));
-    }
+  else { nce(nc_put_var_ulonglong(ncfileid, ncvarid, (unsigned long long *) array)); }
 #endif
 }
 
@@ -698,24 +695,33 @@ write_remap_scrip(const std::string &remapFileIn, const std::string &remapFileOu
 }
 #endif
 
-class ModuleVerifyweights
+class Verifyweights : public Process
 {
-  int operatorID;
-  std::string remapFileIn;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Verifyweights",
+    .operators = { { "verifyweights", 0, 1, "remap filename"}, { "writeremapscrip", 0, 2, "input and output remap filename"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 0, 0, NoRestriction },
+  };
+  inline static RegisterEntry<Verifyweights> registration = RegisterEntry<Verifyweights>(module);
 
   int VERIFYWEIGHTS, WRITEREMAPSCRIP;
+  int operatorID;
+  std::string remapFileIn;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
 #ifndef HAVE_LIBNETCDF
     cdo_abort("NetCDF support not compiled in!");
 #else
-    VERIFYWEIGHTS = cdo_operator_add("verifyweights", 0, 1, "remap file name");
-    WRITEREMAPSCRIP = cdo_operator_add("writeremapscrip", 0, 2, "input and output remap file name");
+    VERIFYWEIGHTS = module.get_id("verifyweights");
+    WRITEREMAPSCRIP = module.get_id("writeremapscrip");
 
     operatorID = cdo_operator_id();
 
@@ -741,17 +747,5 @@ public:
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Verifyweights(void *process)
-{
-  ModuleVerifyweights verifyweights;
-  verifyweights.init(process);
-  verifyweights.run();
-  verifyweights.close();
-
-  return nullptr;
-}
diff --git a/src/Vertcum.cc b/src/Vertcum.cc
index 4e7ddf19c7d8008ccc2c5fbc19c6a15df7a23767..67593e5a6a97012eb382ba8ce929ee795167d379 100644
--- a/src/Vertcum.cc
+++ b/src/Vertcum.cc
@@ -37,8 +37,20 @@ add_vars_mv(size_t gridsize, double missval, const Varray<double> &var1, const V
     }
 }
 
-class ModuleVertcum
+class Vertcum : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertcum",
+    .operators = { { "vertcum"}, { "vertcumhl"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertcum> registration = RegisterEntry<Vertcum>(module);
+  int VERTCUMHL;
   int nlevshl = 0;
 
   CdoStreamID streamID1;
@@ -53,20 +65,16 @@ class ModuleVertcum
   int nvars;
 
   VarList varList1, varList2;
-  std::vector<std::vector<size_t>> varnmiss;
+  std::vector<std::vector<size_t>> varnumMissVals;
   Varray3D<double> vardata1, vardata2;
 
-  int VERTCUMHL;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-                  cdo_operator_add("vertcum",    0,  0, nullptr);
-   VERTCUMHL = cdo_operator_add("vertcumhl",  0,  0, nullptr);
+VERTCUMHL = module.get_id("vertcumhl");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -124,18 +132,17 @@ public:
     varList_init(varList2, vlistID2);
 
     nvars = vlistNvars(vlistID1);
-    varnmiss = std::vector<std::vector<size_t>>(nvars);
+    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
     vardata1 = Varray3D<double>(nvars);
     vardata2 = Varray3D<double>(nvars);
 
-
     for (int varID = 0; varID < nvars; ++varID)
       {
         auto gridsize = varList1[varID].gridsize;
         auto nlevs = varList1[varID].nlevels;
         auto nlevs2 = varList2[varID].nlevels;
 
-        varnmiss[varID].resize(nlevs);
+        varnumMissVals[varID].resize(nlevs);
         vardata1[varID].resize(nlevs);
         vardata2[varID].resize(nlevs2);
         for (int levelID = 0; levelID < nlevs; ++levelID) vardata1[varID][levelID].resize(gridsize);
@@ -166,7 +173,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, &vardata1[varID][levelID][0], &varnmiss[varID][levelID]);
+            cdo_read_record(streamID1, &vardata1[varID][levelID][0], &varnumMissVals[varID][levelID]);
           }
 
         for (int varID = 0; varID < nvars; ++varID)
@@ -218,9 +225,9 @@ public:
             for (int levelID = 0; levelID < nlevs2; ++levelID)
               {
                 auto &single = vardata2[varID][levelID];
-                auto nmiss = varray_num_mv(gridsize, single, missval);
+                auto numMissVals = varray_num_mv(gridsize, single, missval);
                 cdo_def_record(streamID2, varID, levelID);
-                cdo_write_record(streamID2, single.data(), nmiss);
+                cdo_write_record(streamID2, single.data(), numMissVals);
               }
           }
 
@@ -235,17 +242,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-void *
-Vertcum(void *process)
-{
-  ModuleVertcum vertcum;
-  vertcum.init(process);
-  vertcum.run();
-  vertcum.close();
-
-  return nullptr;
-}
diff --git a/src/Vertfillmiss.cc b/src/Vertfillmiss.cc
index e9de1e73fc65408ed53d81ad49df4ae719ce9574..63934629c2ac036ac18a717c24e145c79c551906 100644
--- a/src/Vertfillmiss.cc
+++ b/src/Vertfillmiss.cc
@@ -20,8 +20,20 @@
 #include "pmlist.h"
 #include "fill_1d.h"
 
-class ModuleVertfillmiss
+class Vertfillmiss : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertfillmiss",
+    .operators = { { "vertfillmiss", VertfillmissHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertfillmiss> registration = RegisterEntry<Vertfillmiss>(module);
+
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -44,12 +56,6 @@ private:
   Varray2D<double> dataValues2D;
   Varray<double> levelValues;
 
-  void
-  add_operators()
-  {
-    cdo_operator_add("vertfillmiss", 0, 0, nullptr);
-  }
-
   FillMethod
   convert_fillmethod(const std::string &methodStr)
   {
@@ -70,7 +76,7 @@ private:
 
         KVList kvlist;
         kvlist.name = cdo_module_name();
-        if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+        if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
         if (Options::cdoVerbose) kvlist.print();
 
         for (const auto &kv : kvlist)
@@ -92,12 +98,8 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
-
     get_parameter();
     limit = std::max(limit, 0);
     maxGaps = std::max(maxGaps, 0);
@@ -198,7 +200,7 @@ public:
             if (numLevels > 1)
               {
                 size_t numMissVals = 0;
-                for (int levelID = 0; levelID < numLevels; ++levelID) { numMissVals += vars[varID][levelID].nmiss; }
+                for (int levelID = 0; levelID < numLevels; ++levelID) { numMissVals += vars[varID][levelID].numMissVals; }
                 if (numMissVals > 0) fillmiss(varID);
               }
           }
@@ -226,18 +228,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Vertfillmiss(void *process)
-{
-  ModuleVertfillmiss verfillmiss;
-  verfillmiss.init(process);
-  verfillmiss.run();
-  verfillmiss.close();
-
-  return nullptr;
-}
diff --git a/src/Vertintap.cc b/src/Vertintap.cc
index 7bb789850c42f6257eee76df8a46f6065c672fad..3cef326b6c549817ba1ae2601fb64b6ad46b15c8 100644
--- a/src/Vertintap.cc
+++ b/src/Vertintap.cc
@@ -72,13 +72,32 @@ calc_half_press(const Field3D &fullPress, Field3D &halfPress)
     calc_half_press(fullPress.gridsize, fullPress.nlevels, fullPress.vec_d, halfPress.nlevels, halfPress.vec_d);
 }
 
-class ModuleVertintap
+class Vertintap : public Process
 {
   enum
   {
     func_pl,
     func_hl
   };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertintap",
+    // clang-format off
+    .operators = { { "ap2pl", func_pl, 0, "pressure levels in pascal", VertintapHelp },
+                   { "ap2plx", func_pl, 0, "pressure levels in pascal", VertintapHelp },
+                   { "ap2hl", func_hl, 0, "height levels in meter", VertintapHelp },
+                   { "ap2hlx", func_hl, 0, "height levels in meter", VertintapHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertintap> registration = RegisterEntry<Vertintap>(module);
+
+  int AP2PLX, AP2HLX;
   int apressID_FL = -1, apressID_HL = -1, dpressID = -1;
   int psID = -1;
 
@@ -105,7 +124,7 @@ class ModuleVertintap
   Varray<double> levels;
 
   std::vector<bool> processVars, interpVars;
-  Varray2D<size_t> varnmiss;
+  Varray2D<size_t> varnumMissVals;
   Field3DVector vardata1, vardata2;
 
   Varray<size_t> numMiss_FL, numMiss_HL;
@@ -117,16 +136,10 @@ class ModuleVertintap
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-                     cdo_operator_add("ap2pl",     func_pl, 0, "pressure levels in pascal");
-    auto AP2PLX    = cdo_operator_add("ap2plx",    func_pl, 0, "pressure levels in pascal");
-                     cdo_operator_add("ap2hl",     func_hl, 0, "height levels in meter");
-    auto AP2HLX    = cdo_operator_add("ap2hlx",    func_hl, 0, "height levels in meter");
-    // clang-format on
+    AP2PLX = module.get_id("ap2plx");
+    AP2HLX = module.get_id("ap2hlx");
 
     auto operatorID = cdo_operator_id();
     auto useHeightLevel = (cdo_operator_f1(operatorID) == func_hl);
@@ -141,14 +154,10 @@ public:
         if (useHeightLevel)
           levels = { 10, 50, 100, 500, 1000, 5000, 10000, 15000, 20000, 25000, 30000 };
         else
-          levels = {
-            100000, 92500, 85000, 70000, 60000, 50000, 40000, 30000, 25000, 20000, 15000, 10000, 7000, 5000, 3000, 2000, 1000
-          };
-      }
-    else
-      {
-        levels = cdo_argv_to_flt(cdo_get_oper_argv());
+          levels = { 100000, 92500, 85000, 70000, 60000, 50000, 40000, 30000, 25000,
+                     20000,  15000, 10000, 7000,  5000,  3000,  2000,  1000 };
       }
+    else { levels = cdo_argv_to_flt(cdo_get_oper_argv()); }
 
     numPL = levels.size();
 
@@ -222,7 +231,7 @@ public:
 
     processVars = std::vector<bool>(nvars);
     interpVars = std::vector<bool>(nvars);
-    varnmiss = Varray2D<size_t>(nvars);
+    varnumMissVals = Varray2D<size_t>(nvars);
     vardata1 = Field3DVector(nvars), vardata2 = Field3DVector(nvars);
 
     auto maxLevels = std::max(std::max(numFullLevels, numHalfLevels), numPL);
@@ -269,7 +278,7 @@ public:
 
         if (interpVars[varID])
           {
-            varnmiss[varID].resize(maxLevels, 0);
+            varnumMissVals[varID].resize(maxLevels, 0);
             vardata2[varID].init(varList2[varID]);
           }
         else
@@ -278,7 +287,7 @@ public:
               cdo_warning("Parameter %d has wrong number of levels, skipped! (name=%s nlevel=%d)", varID + 1, var.name,
                           var.nlevels);
 
-            varnmiss[varID].resize(var.nlevels);
+            varnumMissVals[varID].resize(var.nlevels);
           }
       }
 
@@ -313,7 +322,7 @@ public:
           {
             processVars[varID] = false;
             const auto &var = varList1[varID];
-            for (int levelID = 0; levelID < var.nlevels; ++levelID) varnmiss[varID][levelID] = 0;
+            for (int levelID = 0; levelID < var.nlevels; ++levelID) varnumMissVals[varID][levelID] = 0;
           }
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -323,7 +332,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+            cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
@@ -384,7 +393,7 @@ public:
 
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
-                        if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+                        if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
                       }
 
                     const auto &levels3D = (var.nlevels == numFullLevels) ? fullPress : halfPress;
@@ -394,7 +403,7 @@ public:
                     if (!extrapolate)
                       {
                         const auto &numMiss = (var.nlevels == numFullLevels) ? numMiss_FL : numMiss_HL;
-                        varray_copy(numPL, numMiss, varnmiss[varID]);
+                        varray_copy(numPL, numMiss, varnumMissVals[varID]);
                       }
                   }
 
@@ -402,7 +411,7 @@ public:
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
-                                     varnmiss[varID][levelID]);
+                                     varnumMissVals[varID][levelID]);
                   }
               }
           }
@@ -416,17 +425,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Vertintap(void *process)
-{
-  ModuleVertintap verintap;
-  verintap.init(process);
-  verintap.run();
-  verintap.close();
-  return nullptr;
-}
diff --git a/src/Vertintgh.cc b/src/Vertintgh.cc
index aa73b0603aea9cef9f24f5cc056c3f7960673ea7..9815053619a44c9c2ba1b8c707a84ede3ec19460 100644
--- a/src/Vertintgh.cc
+++ b/src/Vertintgh.cc
@@ -60,10 +60,7 @@ create_zaxis_height(Varray<double> &heightLevels)
       else
         cdo_abort("Open failed on %s", zfilename);
     }
-  else
-    {
-      heightLevels = cdo_argv_to_flt(cdo_get_oper_argv());
-    }
+  else { heightLevels = cdo_argv_to_flt(cdo_get_oper_argv()); }
 
   if (zaxisID == CDI_UNDEFID)
     {
@@ -74,8 +71,24 @@ create_zaxis_height(Varray<double> &heightLevels)
   return zaxisID;
 }
 
-class ModuleVertintgh
+class Vertintgh : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertintgh",
+    // clang-format off
+    .operators = { { "gh2hl", 0, 0, "height levels in meter", VertintghHelp },
+                   { "gh2hlx", 0, 0, "height levels in meter", VertintghHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertintgh> registration = RegisterEntry<Vertintgh>(module);
+
+  int GH2HLX;
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -97,25 +110,20 @@ class ModuleVertintgh
 
   VarList varList1;
   VarList varList2;
-  Varray2D<size_t> varnmiss;
+  Varray2D<size_t> varnumMissVals;
   Field3DVector vardata1, vardata2;
 
   Varray<double> heightLevels;
-  Field heightBottom;
 
   Varray<size_t> numMiss_FL, numMiss_HL;
-  std::vector<int> vertIndex_FL, vertIndex_HL;
+  std::vector<int> vertIndex_FL;
+  std::vector<int> vertIndex_HL;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-                  cdo_operator_add("gh2hl",   0, 0, "height levels in meter");
-    auto GH2HLX = cdo_operator_add("gh2hlx",  0, 0, "height levels in meter");
-    // clang-format on
+    GH2HLX = module.get_id("gh2hlx");
 
     auto operatorID = cdo_operator_id();
 
@@ -148,11 +156,8 @@ public:
     for (int varID = 0; varID < nvars; ++varID)
       {
         auto stdname = string_to_lower(cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME));
-
-        // clang-format off
         if (stdname == stdnameHeight_FL) heightID_FL = varID;
         if (stdname == stdnameHeight_HL) heightID_HL = varID;
-        // clang-format on
       }
 
     if (-1 == heightID_FL && -1 == heightID_HL)
@@ -174,10 +179,8 @@ public:
     if (Options::cdoVerbose)
       {
         cdo_print("Found:");
-        // clang-format off
         if (-1 != heightID_FL) cdo_print("  %s -> %s", stdnameHeight_FL, varList1[heightID_FL].name);
         if (-1 != heightID_HL) cdo_print("  %s -> %s", stdnameHeight_HL, varList1[heightID_HL].name);
-        // clang-format on
       }
 
     if (-1 == heightID_FL && -1 == heightID_HL) cdo_abort("%s not found!", stdnameHeight_FL);
@@ -208,7 +211,7 @@ public:
 
     processVars = std::vector<bool>(nvars);
     interpVars = std::vector<bool>(nvars);
-    varnmiss = Varray2D<size_t>(nvars);
+    varnumMissVals = Varray2D<size_t>(nvars);
     vardata1 = Field3DVector(nvars);
     vardata2 = Field3DVector(nvars);
 
@@ -228,7 +231,7 @@ public:
 
         if (interpVars[varID])
           {
-            varnmiss[varID].resize(maxLevels, 0);
+            varnumMissVals[varID].resize(maxLevels, 0);
             vardata2[varID].init(varList2[varID]);
           }
         else
@@ -243,7 +246,7 @@ public:
                   cdo_warning("Parameter %d has wrong number of levels, skipped! (name=%s nlevel=%d)", varID + 1, var.name,
                               var.nlevels);
               }
-            varnmiss[varID].resize(var.nlevels);
+            varnumMissVals[varID].resize(var.nlevels);
           }
       }
 
@@ -260,6 +263,8 @@ public:
   void
   run()
   {
+    Field heightBottom;
+
     int tsID = 0;
     while (true)
       {
@@ -270,7 +275,7 @@ public:
           {
             processVars[varID] = false;
             const auto &var = varList1[varID];
-            for (int levelID = 0; levelID < var.nlevels; ++levelID) varnmiss[varID][levelID] = 0;
+            for (int levelID = 0; levelID < var.nlevels; ++levelID) varnumMissVals[varID][levelID] = 0;
           }
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -280,7 +285,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+            cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
@@ -324,7 +329,7 @@ public:
 
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
-                        if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+                        if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
                       }
 
                     const auto &height3D = (var.nlevels == numFullLevels) ? vardata1[heightID_FL] : vardata1[heightID_HL];
@@ -333,8 +338,8 @@ public:
 
                     if (!extrapolate)
                       {
-                        const auto &numMiss = (var.nlevels == numFullLevels) ? numMiss_FL : numMiss_HL;
-                        varray_copy(heightLevels.size(), numMiss, varnmiss[varID]);
+                        auto numMiss = (var.nlevels == numFullLevels) ? numMiss_FL : numMiss_HL;
+                        varray_copy(heightLevels.size(), numMiss, varnumMissVals[varID]);
                       }
                   }
 
@@ -342,7 +347,7 @@ public:
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
-                                     varnmiss[varID][levelID]);
+                                     varnumMissVals[varID][levelID]);
                   }
               }
           }
@@ -356,17 +361,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Vertintgh(void *process)
-{
-  ModuleVertintgh vertintgh;
-  vertintgh.init(process);
-  vertintgh.run();
-  vertintgh.close();
-  return nullptr;
-}
diff --git a/src/Vertintml.cc b/src/Vertintml.cc
index aec1fab81a31a66d7097c4d0d173a20414e9c02e..7cc5ac8811c935cb58701685f24ab7d278c472af 100644
--- a/src/Vertintml.cc
+++ b/src/Vertintml.cc
@@ -25,6 +25,7 @@
 #include "param_conversion.h"
 #include "vertint_util.h"
 
+/*
 static void
 field_copy_div_2d_to_3d(MemType memType, size_t gridsize, int nlevels, const Field &field2d, Field3D &field3d)
 {
@@ -33,6 +34,28 @@ field_copy_div_2d_to_3d(MemType memType, size_t gridsize, int nlevels, const Fie
   else
     for (size_t i = 0; i < gridsize; ++i) field3d.vec_d[gridsize * nlevels + i] = field2d.vec_d[i] / PlanetGrav;
 }
+*/
+template <typename T>
+static void
+field_check_sgeopot(const Varray<T> &sgeopot, const T *gheightAtSurface)
+{
+  auto len = sgeopot.size();
+  double sumDiff = 0;
+  for (size_t i = 0; i < len; ++i) sumDiff += std::fabs((sgeopot[i] / PlanetGrav) - gheightAtSurface[i]);
+
+  constexpr double lim = 0.1;  // 10cm per grid point
+  if ((sumDiff / len) > lim) cdo_warning("Bottom level of gheight differ from sgeopot/%g (diff=%g)!", PlanetGrav, sumDiff / len);
+  // printf("sumDiff %g %g\n", sumDiff, sumDiff / len);
+}
+
+static void
+field_check_sgeopot(MemType memType, size_t gridsize, int nlevels, const Field &field2d, Field3D &field3d)
+{
+  if (memType == MemType::Float)
+    field_check_sgeopot(field2d.vec_f, &field3d.vec_f[gridsize * (nlevels - 1)]);
+  else
+    field_check_sgeopot(field2d.vec_d, &field3d.vec_d[gridsize * (nlevels - 1)]);
+}
 
 static void
 vct_to_hybrid_pressure(MemType memType, Field3D &pressure_FL, Field3D &pressure_HL, const double *vct, const Field &ps,
@@ -82,9 +105,9 @@ check_range_sgeopot(int stepNum, const Field &sgeopot)
 }
 
 static bool
-zaxis_is_hybrid(int zaxistype)
+zaxis_is_hybrid(int zaxisType)
 {
-  return (zaxistype == ZAXIS_HYBRID || zaxistype == ZAXIS_HYBRID_HALF);
+  return (zaxisType == ZAXIS_HYBRID || zaxisType == ZAXIS_HYBRID_HALF);
 }
 
 static void
@@ -95,9 +118,9 @@ change_hybrid_zaxis(int vlistID1, int vlistID2, int vctSize, double *vct, int za
     {
       auto zaxisID = vlistZaxis(vlistID1, iz);
       auto nlevels = zaxisInqSize(zaxisID);
-      auto zaxistype = zaxisInqType(zaxisID);
+      auto zaxisType = zaxisInqType(zaxisID);
 
-      if (zaxis_is_hybrid(zaxistype) && (nlevels == numHalfLevels || nlevels == numFullLevels))
+      if (zaxis_is_hybrid(zaxisType) && (nlevels == numHalfLevels || nlevels == numFullLevels))
         {
           auto vctSize2 = zaxisInqVctSize(zaxisID);
           if (vctSize2 == vctSize && memcmp(vct, zaxisInqVctPtr(zaxisID), vctSize * sizeof(double)) == 0)
@@ -163,28 +186,23 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
   auto nvars = vlistNvars(vlistID1);
 
   std::vector<bool> processVars(nvars), interpVars(nvars);
-  Varray2D<size_t> varnmiss(nvars);
+  Varray2D<size_t> varnumMissVals(nvars);
   Field3DVector vardata1(nvars), vardata2(nvars);
 
   auto maxLevels = std::max(numHalfLevels, numPL);
 
-  Varray<size_t> pnmiss;
-  if (!extrapolate) pnmiss.resize(numPL);
+  Varray<size_t> pnumMissVals;
+  if (!extrapolate) pnumMissVals.resize(numPL);
 
   // check levels
   if (zaxisID_ML != -1)
     {
-      auto nlev = zaxisInqSize(zaxisID_ML);
-      if (nlev != numHybridLevels) cdo_abort("Internal error, wrong number of hybrid level!");
+      if (zaxisInqSize(zaxisID_ML) != numHybridLevels) cdo_abort("Internal error, wrong number of hybrid level!");
     }
 
-  std::vector<int> vertIndex;
-
   Field3D pressure_FL, pressure_HL;
   if (zaxisID_ML != -1 && gridsize > 0)
     {
-      vertIndex.resize(gridsize * numPL);
-
       CdoVar var3Df, var3Dh;
       var3Df.gridsize = gridsize;
       var3Df.nlevels = numFullLevels;
@@ -212,6 +230,14 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
 
   VarIDs varIDs = search_varIDs(varList1, vlistID1, numFullLevels);
 
+  // vertical_interp_Z() is implemented for gheight on model half levels only
+  if (-1 != varIDs.gheightID && varList1[varIDs.gheightID].nlevels == numFullLevels)
+    {
+      if (Options::cdoVerbose)
+        cdo_print("%s(%s) on model full levels found!", var_stdname(geopotential_height), varList1[varIDs.gheightID].name);
+      varIDs.gheightID = -1;
+    }
+
   if (Options::cdoVerbose)
     {
       cdo_print("Found:");
@@ -228,38 +254,52 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
   for (int varID = 0; varID < nvars; ++varID)
     {
       auto &var1 = varList1[varID];
-      auto gridID = var1.gridID;
-      auto zaxisID = var1.zaxisID;
-      auto zaxistype = zaxisInqType(zaxisID);
       auto nlevels = var1.nlevels;
 
-      if (gridInqType(gridID) == GRID_SPECTRAL && zaxis_is_hybrid(zaxistype))
-        cdo_abort("Spectral data on model level unsupported!");
+      if (var1.gridType == GRID_SPECTRAL && zaxis_is_hybrid(var1.zaxisType)) cdo_abort("Spectral data on model level unsupported!");
 
-      if (gridInqType(gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
+      if (var1.gridType == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
 
-      if (varID == varIDs.gheightID) var1.nlevels = nlevels + 1;
+      // if (varID == varIDs.gheightID) var1.nlevels = nlevels + 1;
       vardata1[varID].init(var1);
-      if (varID == varIDs.gheightID) var1.nlevels = nlevels;
+      // if (varID == varIDs.gheightID) var1.nlevels = nlevels;
 
-      // interpVars[varID] = (zaxis_is_hybrid(zaxistype) && zaxisID_ML != -1 && nlevels == numHybridLevels);
+      // interpVars[varID] = (zaxis_is_hybrid(var1.zaxisType) && zaxisID_ML != -1 && nlevels == numHybridLevels);
       interpVars[varID]
-          = (zaxisID == zaxisID_ML
-             || (zaxis_is_hybrid(zaxistype) && zaxisID_ML != -1 && (nlevels == numHalfLevels || nlevels == numFullLevels)));
+          = (var1.zaxisID == zaxisID_ML
+             || (zaxis_is_hybrid(var1.zaxisType) && zaxisID_ML != -1 && (nlevels == numHalfLevels || nlevels == numFullLevels)));
 
       if (interpVars[varID])
         {
-          varnmiss[varID].resize(maxLevels, 0);
+          varnumMissVals[varID].resize(maxLevels, 0);
           vardata2[varID].init(varList2[varID]);
         }
       else
         {
-          varnmiss[varID].resize(nlevels);
-          if (zaxis_is_hybrid(zaxistype) && zaxisID_ML != -1 && nlevels > 1)
+          varnumMissVals[varID].resize(nlevels);
+          if (zaxis_is_hybrid(var1.zaxisType) && zaxisID_ML != -1 && nlevels > 1)
             cdo_warning("Parameter %d has wrong number of levels, skipped! (param=%s nlevel=%d)", varID + 1, var1.name, nlevels);
         }
     }
 
+  auto needVertIndexHalf{ false };
+  for (int varID = 0; varID < nvars; ++varID)
+    {
+      if (interpVars[varID])
+        {
+          auto &var1 = varList1[varID];
+          if (var1.nlevels == numHalfLevels && varID != varIDs.gheightID) needVertIndexHalf = true;
+        }
+    }
+
+  std::vector<int> vertIndex_FL;
+  std::vector<int> vertIndex_HL;
+  if (zaxisID_ML != -1 && gridsize > 0)
+    {
+      vertIndex_FL.resize(gridsize * numPL);
+      if (needVertIndexHalf) vertIndex_HL.resize(gridsize * numPL);
+    }
+
   if (zaxisID_ML != -1 && varIDs.gheightID != -1 && varIDs.tempID == -1)
     cdo_abort("%s not found, needed for vertical interpolation of %s!", var_stdname(air_temperature),
               var_stdname(geopotential_height));
@@ -324,7 +364,7 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
 
           if (vctIsInverted && zaxisID_ML != -1 && var.zaxisID == zaxisID_ML) levelID = var.nlevels - 1 - levelID;
 
-          cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+          cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
 
           processVars[varID] = true;
         }
@@ -352,9 +392,14 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
 
           vct_to_hybrid_pressure(memType, pressure_FL, pressure_HL, vct.data(), psProg, numFullLevels, gridsize);
 
-          gen_vert_index(vertIndex, pressureLevels, pressure_FL, gridsize);
+          gen_vert_index(vertIndex_FL, pressureLevels, pressure_FL, gridsize);
+          if (!extrapolate) gen_vert_index_mv(vertIndex_FL, pressureLevels, gridsize, psProg, pnumMissVals);
 
-          if (!extrapolate) gen_vert_index_mv(vertIndex, pressureLevels, gridsize, psProg, pnmiss);
+          if (needVertIndexHalf)
+            {
+              gen_vert_index(vertIndex_HL, pressureLevels, pressure_HL, gridsize);
+              if (!extrapolate) gen_vert_index_mv(vertIndex_HL, pressureLevels, gridsize, psProg, pnumMissVals);
+            }
         }
 
       for (int varID = 0; varID < nvars; ++varID)
@@ -367,43 +412,44 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
 
               if (interpVars[varID])
                 {
-                  auto nlevels = var.nlevels;
-                  if (nlevels != numFullLevels && nlevels != numHalfLevels)
+                  if (var.nlevels != numFullLevels && var.nlevels != numHalfLevels)
                     cdo_abort("Number of hybrid level differ from full/half level (param=%s)!", var.name);
 
-                  for (int levelID = 0; levelID < nlevels; ++levelID)
+                  for (int levelID = 0; levelID < var.nlevels; ++levelID)
                     {
-                      if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+                      if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
                     }
 
                   if (varID == varIDs.tempID)
                     {
-                      if (nlevels == numHalfLevels) cdo_abort("Temperature on half level unsupported!");
+                      if (var.nlevels == numHalfLevels) cdo_abort("Temperature on half level unsupported!");
 
-                      vertical_interp_T(nlevels, pressure_FL, pressure_HL, vardata1[varID], vardata2[varID], sgeopot, vertIndex,
-                                        pressureLevels, gridsize);
+                      vertical_interp_T(var.nlevels, pressure_FL, pressure_HL, vardata1[varID], vardata2[varID], sgeopot,
+                                        vertIndex_FL, pressureLevels, gridsize);
                     }
                   else if (varID == varIDs.gheightID)
                     {
-                      field_copy_div_2d_to_3d(memType, gridsize, nlevels, sgeopot, vardata1[varID]);
+                      // field_copy_div_2d_to_3d(memType, gridsize, var.nlevels, sgeopot, vardata1[varID]);
+                      field_check_sgeopot(memType, gridsize, var.nlevels, sgeopot, vardata1[varID]);
 
-                      vertical_interp_Z(nlevels, pressure_FL, pressure_HL, vardata1[varID], vardata2[varID],
-                                        vardata1[varIDs.tempID], sgeopot, vertIndex, pressureLevels, gridsize);
+                      vertical_interp_Z(numFullLevels, pressure_FL, pressure_HL, vardata1[varID], vardata2[varID],
+                                        vardata1[varIDs.tempID], sgeopot, vertIndex_FL, pressureLevels, gridsize);
                     }
                   else
                     {
-                      const auto &levels3D = (nlevels == numFullLevels) ? pressure_FL : pressure_HL;
-                      vertical_interp_X(levels3D, vardata1[varID], vardata2[varID], vertIndex, pressureLevels, gridsize);
+                      const auto &levels3D = (var.nlevels == numFullLevels) ? pressure_FL : pressure_HL;
+                      const auto &vertIndex3D = (var.nlevels == numFullLevels) ? vertIndex_FL : vertIndex_HL;
+                      vertical_interp_X(levels3D, vardata1[varID], vardata2[varID], vertIndex3D, pressureLevels, gridsize);
                     }
 
-                  if (!extrapolate) varray_copy(numPL, pnmiss, varnmiss[varID]);
+                  if (!extrapolate) varray_copy(numPL, pnumMissVals, varnumMissVals[varID]);
                 }
 
               for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
                 {
                   cdo_def_record(streamID2, varID, levelID);
                   cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
-                                   varnmiss[varID][levelID]);
+                                   varnumMissVals[varID][levelID]);
                 }
             }
         }
@@ -415,10 +461,14 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
   cdo_stream_close(streamID1);
 }
 
+//#define ENABLE_HEIGHT_LEVEL_INTERPOLATION
+
+#ifdef ENABLE_HEIGHT_LEVEL_INTERPOLATION
+// obsolete, use intlevel!!!
 static void
 height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 {
-  int numHL = heightLevels.size();
+  int numHeightLevels = heightLevels.size();
 
   auto streamID1 = cdo_open_read(0);
 
@@ -436,7 +486,7 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 
   auto gridsize = vlist_check_gridsize(vlistID1);
 
-  auto zaxisID_HL = zaxisCreate(ZAXIS_HEIGHT, numHL);
+  auto zaxisID_HL = zaxisCreate(ZAXIS_HEIGHT, numHeightLevels);
   zaxisDefLevels(zaxisID_HL, heightLevels.data());
 
   int zaxisID_ML = -1;
@@ -452,7 +502,7 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
   VarList varList2;
   varList_init(varList2, vlistID2);
   varListSetMemtype(varList2, memType);
-
+  /*
   auto vctIsInverted = false;
   if (vctSize && vctSize % 2 == 0)
     {
@@ -467,17 +517,17 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
   if (Options::cdoVerbose) cdo_print("vctIsInverted = %d", static_cast<int>(vctIsInverted));
 
   if (vctIsInverted) invert_vct(vct);
-
+  */
   auto nvars = vlistNvars(vlistID1);
 
   std::vector<bool> processVars(nvars), interpVars(nvars);
-  Varray2D<size_t> varnmiss(nvars);
+  Varray2D<size_t> varnumMissVals(nvars);
   Field3DVector vardata1(nvars), vardata2(nvars);
 
-  auto maxLevels = std::max(numHalfLevels, numHL);
+  auto maxLevels = std::max(numHalfLevels, numHeightLevels);
 
-  Varray<size_t> pnmiss;
-  if (!extrapolate) pnmiss.resize(numHL);
+  Varray<size_t> pnumMissVals;
+  if (!extrapolate) pnumMissVals.resize(numHeightLevels);
 
   // check levels
   if (zaxisID_ML != -1)
@@ -487,23 +537,7 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
     }
 
   std::vector<int> vertIndex;
-
-  Field3D pressure_FL, pressure_HL;
-  if (zaxisID_ML != -1 && gridsize > 0)
-    {
-      vertIndex.resize(gridsize * numHL);
-
-      CdoVar var3Df, var3Dh;
-      var3Df.gridsize = gridsize;
-      var3Df.nlevels = numFullLevels;
-      var3Df.memType = memType;
-      pressure_FL.init(var3Df);
-
-      var3Dh.gridsize = gridsize;
-      var3Dh.nlevels = numHalfLevels;
-      var3Dh.memType = memType;
-      pressure_HL.init(var3Dh);
-    }
+  if (zaxisID_ML != -1 && gridsize > 0) { vertIndex.resize(gridsize * numHeightLevels); }
   else
     cdo_warning("No 3D variable with hybrid sigma pressure coordinate found!");
 
@@ -524,39 +558,35 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 
   for (int varID = 0; varID < nvars; ++varID)
     {
-      auto &var1 = varList1[varID];
-      auto gridID = var1.gridID;
-      auto zaxisID = var1.zaxisID;
-      auto zaxistype = zaxisInqType(zaxisID);
+      const auto &var1 = varList1[varID];
       auto nlevels = var1.nlevels;
 
-      if (gridInqType(gridID) == GRID_SPECTRAL && zaxis_is_hybrid(zaxistype))
-        cdo_abort("Spectral data on model level unsupported!");
+      if (var1.gridType == GRID_SPECTRAL && zaxis_is_hybrid(var1.zaxisType)) cdo_abort("Spectral data on model level unsupported!");
 
-      if (gridInqType(gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
+      if (var1.gridType == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
 
       vardata1[varID].init(var1);
 
-      // interpVars[varID] = (zaxis_is_hybrid(zaxistype) && zaxisID_ML != -1 && nlevels == numHybridLevels);
+      // interpVars[varID] = (zaxis_is_hybrid(var1.zaxisType) && zaxisID_ML != -1 && nlevels == numHybridLevels);
       interpVars[varID]
-          = (zaxisID == zaxisID_ML
-             || (zaxis_is_hybrid(zaxistype) && zaxisID_ML != -1 && (nlevels == numHalfLevels || nlevels == numFullLevels)));
+          = (var1.zaxisID == zaxisID_ML
+             || (zaxis_is_hybrid(var1.zaxisType) && zaxisID_ML != -1 && (nlevels == numHalfLevels || nlevels == numFullLevels)));
 
       if (interpVars[varID])
         {
-          varnmiss[varID].resize(maxLevels, 0);
+          varnumMissVals[varID].resize(maxLevels, 0);
           vardata2[varID].init(varList2[varID]);
         }
       else
         {
-          varnmiss[varID].resize(nlevels);
-          if (zaxis_is_hybrid(zaxistype) && zaxisID_ML != -1 && nlevels > 1)
+          varnumMissVals[varID].resize(nlevels);
+          if (zaxis_is_hybrid(var1.zaxisType) && zaxisID_ML != -1 && nlevels > 1)
             cdo_warning("Parameter %d has wrong number of levels, skipped! (param=%s nlevel=%d)", varID + 1, var1.name, nlevels);
         }
     }
 
   if (zaxisID_ML != -1 && varIDs.gheightID == -1) cdo_abort("%s not found!", var_stdname(geopotential_height));
-
+  /*
   auto sgeopotNeeded = (!extrapolate && varIDs.gheightID != -1);
 
   Field sgeopot;
@@ -572,17 +602,16 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 
           if (extrapolate) cdo_warning("%s not found - set to zero!", var_stdname(surface_geopotential));
         }
-      else
-        {
-          sgeopot.init(varList1[varIDs.sgeopotID]);
-        }
+      else { sgeopot.init(varList1[varIDs.sgeopotID]); }
 
       field_fill(sgeopot, 0.0);
     }
-
+  */
   auto streamID2 = cdo_open_write(1);
   cdo_def_vlist(streamID2, vlistID2);
 
+  Field heightBottom;
+
   int tsID = 0;
   while (true)
     {
@@ -598,16 +627,21 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
         {
           int varID, levelID;
           cdo_inq_record(streamID1, &varID, &levelID);
-          cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+          cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
 
           processVars[varID] = true;
         }
 
+      auto lreverse = true;
       if (zaxisID_ML != -1)
         {
-          gen_vert_index(vertIndex, heightLevels, vardata1[varIDs.gheightID], gridsize);
-
-          // if (!extrapolate) gen_vert_index_mv(vertIndex, heightLevels, gridsize, psProg, pnmiss);
+          gen_vert_index(vertIndex, heightLevels, vardata1[varIDs.gheightID], gridsize, lreverse);
+          if (!extrapolate)
+            {
+              heightBottom.init(varList1[varIDs.gheightID]);
+              field_copy(vardata1[varIDs.gheightID], numFullLevels - 1, heightBottom);
+              gen_vert_index_mv(vertIndex, heightLevels, gridsize, heightBottom, pnumMissVals, lreverse);
+            }
         }
 
       for (int varID = 0; varID < nvars; ++varID)
@@ -624,21 +658,20 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 
                   for (int levelID = 0; levelID < var.nlevels; ++levelID)
                     {
-                      if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+                      if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
                     }
 
-                  // const auto &levels3D = (nlevels == numFullLevels) ? vardata1[varIDs.gheightID] : pressure_HL;
                   const auto &levels3D = vardata1[varIDs.gheightID];
                   vertical_interp_X(levels3D, vardata1[varID], vardata2[varID], vertIndex, heightLevels, gridsize);
 
-                  if (!extrapolate) varray_copy(numHL, pnmiss, varnmiss[varID]);
+                  if (!extrapolate) varray_copy(numHeightLevels, pnumMissVals, varnumMissVals[varID]);
                 }
 
               for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
                 {
                   cdo_def_record(streamID2, varID, levelID);
                   cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
-                                   varnmiss[varID][levelID]);
+                                   varnumMissVals[varID][levelID]);
                 }
             }
         }
@@ -649,8 +682,9 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
   cdo_stream_close(streamID2);
   cdo_stream_close(streamID1);
 }
+#endif
 
-class ModuleVertintml
+class Vertintml : public Process
 {
   enum
   {
@@ -658,32 +692,49 @@ class ModuleVertintml
     func_hl
   };
 
-  bool doPressureInterpolation;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertintml",
+    // clang-format off
+    .operators = { { "ml2pl", func_pl, 0, "pressure levels in pascal", VertintmlHelp },
+                   { "ml2hl", func_hl, 0, "height levels in meter", VertintmlHelp },
+                   { "ml2plx", func_pl, 0, "pressure levels in pascal", VertintmlHelp },
+                   { "ml2hlx", func_hl, 0, "height levels in meter", VertintmlHelp },
+#ifdef ENABLE_HEIGHT_LEVEL_INTERPOLATION
+                   { "ml2height", func_hl, 1, "height levels in meter", VertintmlHelp },
+                   { "ml2heightx", func_hl, 1, "height levels in meter", VertintmlHelp }
+#endif
+                 },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertintml> registration = RegisterEntry<Vertintml>(module);
+
+  int ML2PLX, ML2HLX, ML2HEIGHTX;
+
+  bool doHeightInterpolation;
   bool useHeightLevel;
   bool extrapolate;
 
   Varray<double> levels;
 
-  int ML2PLX, ML2HLX, ML2HEIGHTX;
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-                 cdo_operator_add("ml2pl",      func_pl, 0, "pressure levels in pascal");
-    ML2PLX     = cdo_operator_add("ml2plx",     func_pl, 0, "pressure levels in pascal");
-                 cdo_operator_add("ml2hl",      func_hl, 0, "height levels in meter");
-    ML2HLX     = cdo_operator_add("ml2hlx",     func_hl, 0, "height levels in meter");
-                 cdo_operator_add("ml2height",  func_hl, 1, "height levels in meter");
-    ML2HEIGHTX = cdo_operator_add("ml2heightx", func_hl, 1, "height levels in meter");
-    // clang-format on
+    ML2PLX = module.get_id("ml2plx");
+    ML2HLX = module.get_id("ml2hlx");
+#ifdef ENABLE_HEIGHT_LEVEL_INTERPOLATION
+    ML2HEIGHTX = module.get_id("ml2heightx");
+#endif
 
     auto operatorID = cdo_operator_id();
     useHeightLevel = (cdo_operator_f1(operatorID) == func_hl);
-    doPressureInterpolation = (cdo_operator_f2(operatorID) == 0);
+    doHeightInterpolation = (cdo_operator_f2(operatorID) == 1);
 
     extrapolate = (operatorID == ML2PLX || operatorID == ML2HLX || operatorID == ML2HEIGHTX);
     if (extrapolate == false) extrapolate = getenv_extrapolate();
@@ -698,34 +749,22 @@ public:
           levels = { 100000, 92500, 85000, 70000, 60000, 50000, 40000, 30000, 25000,
                      20000,  15000, 10000, 7000,  5000,  3000,  2000,  1000 };
       }
-    else
-      {
-        levels = cdo_argv_to_flt(cdo_get_oper_argv());
-      }
+    else { levels = cdo_argv_to_flt(cdo_get_oper_argv()); }
   }
 
   void
   run()
   {
-    if (doPressureInterpolation)
-      pressure_level_interpolation(levels, useHeightLevel, extrapolate);
-    else
+#ifdef ENABLE_HEIGHT_LEVEL_INTERPOLATION
+    if (doHeightInterpolation)
       height_level_interpolation(levels, extrapolate);
+    else
+#endif
+      pressure_level_interpolation(levels, useHeightLevel, extrapolate);
   }
 
   void
   close()
   {
-    cdo_finish();
   }
 };
-
-void *
-Vertintml(void *process)
-{
-  ModuleVertintml vertintml;
-  vertintml.init(process);
-  vertintml.run();
-  vertintml.close();
-  return nullptr;
-}
diff --git a/src/Vertintzs.cc b/src/Vertintzs.cc
index 1693285970a59584b5d8a9940aad2a0e45eed82f..5f69b64e55e3865969c56ef7e57343ca36c2f600 100644
--- a/src/Vertintzs.cc
+++ b/src/Vertintzs.cc
@@ -52,10 +52,7 @@ create_zaxis_depth(Varray<double> &depthLevels)
       else
         cdo_abort("Open failed on %s", zfilename);
     }
-  else
-    {
-      depthLevels = cdo_argv_to_flt(cdo_get_oper_argv());
-    }
+  else { depthLevels = cdo_argv_to_flt(cdo_get_oper_argv()); }
 
   if (zaxisID == CDI_UNDEFID)
     {
@@ -66,8 +63,20 @@ create_zaxis_depth(Varray<double> &depthLevels)
   return zaxisID;
 }
 
-class ModuleVertintzs
+class Vertintzs : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertintzs",
+    .operators = { { "zs2zl", 0, 0, "depth levels in meter"}, { "zs2zlx"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertintzs> registration = RegisterEntry<Vertintzs>(module);
+
 private:
   int nvars;
   std::vector<bool> processVars;
@@ -84,7 +93,7 @@ private:
   Field3DVector vardata1;
   Field3DVector vardata2;
 
-  Varray2D<size_t> varnmiss;
+  Varray2D<size_t> varnumMissVals;
 
   int depthID = -1;
 
@@ -99,15 +108,12 @@ private:
   Field depthBottom;
   int numFullLevels;
 
-  Varray<size_t> pnmiss;
+  Varray<size_t> pnumMissVals;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    cdo_operator_add("zs2zl", 0, 0, "depth levels in meter");
 
     auto operatorID = cdo_operator_id();
 
@@ -162,13 +168,13 @@ public:
     varList_init(varList2, vlistID2);
     varListSetMemtype(varList2, memType);
 
-    if (!extrapolate) pnmiss.resize(depthLevels.size());
+    if (!extrapolate) pnumMissVals.resize(depthLevels.size());
 
     vertIndexFull.resize(gridsize * depthLevels.size());
 
     processVars = std::vector<bool>(nvars);
     interpVars = std::vector<bool>(nvars);
-    varnmiss = Varray2D<size_t>(nvars);
+    varnumMissVals = Varray2D<size_t>(nvars);
     vardata1 = Field3DVector(nvars);
     vardata2 = Field3DVector(nvars);
 
@@ -181,7 +187,7 @@ public:
         if (gridInqType(var.gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
 
         vardata1[varID].init(var);
-        varnmiss[varID].resize(maxlev, 0);
+        varnumMissVals[varID].resize(maxlev, 0);
 
         interpVars[varID] = (var.zaxisID == zaxisIDfull || (is_depth_axis(var.zaxisID) && (var.nlevels == numFullLevels)));
 
@@ -218,7 +224,7 @@ public:
           {
             processVars[varID] = false;
             const auto &var = varList1[varID];
-            for (int levelID = 0; levelID < var.nlevels; ++levelID) varnmiss[varID][levelID] = 0;
+            for (int levelID = 0; levelID < var.nlevels; ++levelID) varnumMissVals[varID][levelID] = 0;
           }
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -228,7 +234,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vardata1[varID], levelID, &varnmiss[varID][levelID]);
+            cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
@@ -244,7 +250,7 @@ public:
               {
                 depthBottom.init(varList1[depthID]);
                 field_copy(fullDepth, numFullLevels - 1, depthBottom);
-                gen_vert_index_mv(vertIndexFull, depthLevels, gridsize, depthBottom, pnmiss, lreverse);
+                gen_vert_index_mv(vertIndexFull, depthLevels, gridsize, depthBottom, pnumMissVals, lreverse);
               }
           }
 
@@ -263,19 +269,19 @@ public:
 
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
-                        if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+                        if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
                       }
 
                     vertical_interp_X(fullDepth, vardata1[varID], vardata2[varID], vertIndexFull, depthLevels, gridsize);
 
-                    if (!extrapolate) varray_copy(depthLevels.size(), pnmiss, varnmiss[varID]);
+                    if (!extrapolate) varray_copy(depthLevels.size(), pnumMissVals, varnumMissVals[varID]);
                   }
 
                 for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     auto varout = (interpVars[varID] ? vardata2[varID] : vardata1[varID]);
-                    cdo_write_record(streamID2, varout, levelID, varnmiss[varID][levelID]);
+                    cdo_write_record(streamID2, varout, levelID, varnumMissVals[varID][levelID]);
                   }
               }
           }
@@ -289,17 +295,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Vertintzs(void *process)
-{
-  ModuleVertintzs vertintzs;
-  vertintzs.init(process);
-  vertintzs.run();
-  vertintzs.close();
-  return nullptr;
-}
diff --git a/src/Vertstat.cc b/src/Vertstat.cc
index 95c7694df3f9ff2386760700cb3f0bf701e2981d..01c4a7afea39ae3a2ce7064dd8610d6a1a8cd404 100644
--- a/src/Vertstat.cc
+++ b/src/Vertstat.cc
@@ -76,7 +76,7 @@ vertstat_get_parameter(bool &weights, bool &genbounds)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -95,8 +95,31 @@ vertstat_get_parameter(bool &weights, bool &genbounds)
     }
 }
 
-class ModuleVertstat
+class Vertstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertstat",
+    .operators = { { "vertrange", FieldFunc_Range, 0, VertstatHelp },
+                   { "vertmin", FieldFunc_Min, 0, VertstatHelp },
+                   { "vertmax", FieldFunc_Max, 0, VertstatHelp },
+                   { "vertsum", FieldFunc_Sum, 0, VertstatHelp },
+                   { "vertint", FieldFunc_Sum, 1, VertstatHelp },
+                   { "vertmean", FieldFunc_Mean, 1, VertstatHelp },
+                   { "vertavg", FieldFunc_Avg, 1, VertstatHelp },
+                   { "vertstd", FieldFunc_Std, 1, VertstatHelp },
+                   { "vertstd1", FieldFunc_Std1, 1, VertstatHelp },
+                   { "vertvar", FieldFunc_Var, 1, VertstatHelp },
+                   { "vertvar1", FieldFunc_Var1, 1, VertstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertstat> registration = RegisterEntry<Vertstat>(module);
+
+  int VERTINT;
   struct VertInfo
   {
     int zaxisID = -1;
@@ -136,27 +159,12 @@ class ModuleVertstat
   Field field;
 
   std::vector<VertInfo> vert;
-  int VERTINT;
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    // clang-format off
-               cdo_operator_add("vertrange", FieldFunc_Range, 0, nullptr);
-               cdo_operator_add("vertmin",   FieldFunc_Min,   0, nullptr);
-               cdo_operator_add("vertmax",   FieldFunc_Max,   0, nullptr);
-               cdo_operator_add("vertsum",   FieldFunc_Sum,   0, nullptr);
-    VERTINT  = cdo_operator_add("vertint",   FieldFunc_Sum,   1, nullptr);
-               cdo_operator_add("vertmean",  FieldFunc_Mean,  1, nullptr);
-               cdo_operator_add("vertavg",   FieldFunc_Avg,   1, nullptr);
-               cdo_operator_add("vertvar",   FieldFunc_Var,   1, nullptr);
-               cdo_operator_add("vertvar1",  FieldFunc_Var1,  1, nullptr);
-               cdo_operator_add("vertstd",   FieldFunc_Std,   1, nullptr);
-               cdo_operator_add("vertstd1",  FieldFunc_Std1,  1, nullptr);
-    // clang-format on
+    VERTINT = module.get_id("vertint");
 
     operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -254,7 +262,6 @@ public:
   void
   run()
   {
-
     auto field2_stdvar_func = lstd ? field2_std : field2_var;
     auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
     int tsID = 0;
@@ -310,7 +317,7 @@ public:
                 cdo_read_record(streamID1, rvars1);
                 if (lrange)
                   {
-                    vars2[varID].nmiss = rvars1.nmiss;
+                    vars2[varID].numMissVals = rvars1.numMissVals;
                     vars2[varID].vec_d = rvars1.vec_d;
                   }
 
@@ -327,7 +334,7 @@ public:
                     else { field2_moq(vars2[varID], rvars1); }
                   }
 
-                if (rvars1.nmiss || !rsamp1.empty() || needWeights)
+                if (rvars1.numMissVals || !rsamp1.empty() || needWeights)
                   {
                     if (rsamp1.empty()) rsamp1.resize(gridsize);
 
@@ -343,7 +350,7 @@ public:
                 if (operatorID == VERTINT && is_not_equal(layerThickness, 1.0)) fieldc_mul(field, layerThickness);
                 if (lmean && is_not_equal(layerWeight, 1.0)) fieldc_mul(field, layerWeight);
 
-                if (field.nmiss || !rsamp1.empty())
+                if (field.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(gridsize, rvars1.nsamp);
 
@@ -414,19 +421,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
-
   }
 };
-
-void *
-Vertstat(void *process)
-{
-  ModuleVertstat vertstat;
-  vertstat.init(process);
-  vertstat.run();
-  vertstat.close();
-
-  return nullptr;
-}
diff --git a/src/Vertwind.cc b/src/Vertwind.cc
index 1b8ca443a58e82b8d6cd79e95401d2499e895a4a..c79e0008be82e4311a0d3e5a823a872084debbac 100644
--- a/src/Vertwind.cc
+++ b/src/Vertwind.cc
@@ -23,8 +23,19 @@
 constexpr double R = 287.07;   // spezielle Gaskonstante fuer Luft
 constexpr double G = 9.80665;  // Erdbeschleunigung
 
-class ModuleVertwind
+class Vertwind : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Vertwind",
+    .operators = { { "vertwind"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Vertwind> registration = RegisterEntry<Vertwind>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -54,11 +65,9 @@ class ModuleVertwind
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -84,33 +93,15 @@ public:
             auto varname = string_to_lower(varList1[varID].name);
 
             if (varname == "st") { code = temp_code; }
-            else if (varname == "sq")
-              {
-                code = sq_code;
-              }
-            else if (varname == "aps")
-              {
-                code = ps_code;
-              }
-            else if (varname == "omega")
-              {
-                code = omega_code;
-              }
+            else if (varname == "sq") { code = sq_code; }
+            else if (varname == "aps") { code = ps_code; }
+            else if (varname == "omega") { code = omega_code; }
           }
 
         if (code == temp_code) { tempID = varID; }
-        else if (code == sq_code)
-          {
-            sqID = varID;
-          }
-        else if (code == ps_code)
-          {
-            psID = varID;
-          }
-        else if (code == omega_code)
-          {
-            omegaID = varID;
-          }
+        else if (code == sq_code) { sqID = varID; }
+        else if (code == ps_code) { psID = varID; }
+        else if (code == omega_code) { omegaID = varID; }
       }
 
     if (tempID == -1 || sqID == -1 || omegaID == -1)
@@ -205,20 +196,20 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            size_t nmiss;
+            size_t numMissVals;
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
             auto offset = (size_t) levelID * gridsize;
 
             if (varID == tempID)
-              cdo_read_record(streamID1, &temp[offset], &nmiss);
+              cdo_read_record(streamID1, &temp[offset], &numMissVals);
             else if (varID == sqID)
-              cdo_read_record(streamID1, &sq[offset], &nmiss);
+              cdo_read_record(streamID1, &sq[offset], &numMissVals);
             else if (varID == omegaID)
-              cdo_read_record(streamID1, &omega[offset], &nmiss);
+              cdo_read_record(streamID1, &omega[offset], &numMissVals);
             else if (varID == psID && zaxisInqType(zaxisID) == ZAXIS_HYBRID)
-              cdo_read_record(streamID1, psProg.data(), &nmiss);
+              cdo_read_record(streamID1, psProg.data(), &numMissVals);
           }
 
         if (zaxisInqType(zaxisID) == ZAXIS_HYBRID)
@@ -255,12 +246,12 @@ public:
           {
             auto offset = (size_t) levelID * gridsize;
 
-            size_t nmiss_out = 0;
+            size_t numMissVals_out = 0;
             for (size_t i = 0; i < gridsize; ++i)
-              if (dbl_is_equal(wms[offset + i], missval_out)) nmiss_out++;
+              if (dbl_is_equal(wms[offset + i], missval_out)) numMissVals_out++;
 
             cdo_def_record(streamID2, 0, levelID);
-            cdo_write_record(streamID2, &wms[offset], nmiss_out);
+            cdo_write_record(streamID2, &wms[offset], numMissVals_out);
           }
 
         tsID++;
@@ -274,18 +265,5 @@ public:
     cdo_stream_close(streamID1);
 
     vlistDestroy(vlistID2);
-
-    cdo_finish();
   }
 };
-
-void *
-Vertwind(void *process)
-{
-  ModuleVertwind vertwind;
-  vertwind.init(process);
-  vertwind.run();
-  vertwind.close();
-
-  return nullptr;
-}
diff --git a/src/Wct.cc b/src/Wct.cc
index 53e581656b7cf43de5fb189a456348b13d6c8716..b6abb14fcee1df8af71e79e57bbf642b860612e3 100644
--- a/src/Wct.cc
+++ b/src/Wct.cc
@@ -50,7 +50,7 @@ farexpr(Field &field1, Field &field2, double (*expression)(double, double, doubl
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (DBL_IS_EQUAL(field1.vec_d[i], missval1) || DBL_IS_EQUAL(field2.vec_d[i], missval2))
@@ -63,11 +63,23 @@ farexpr(Field &field1, Field &field2, double (*expression)(double, double, doubl
       for (size_t i = 0; i < len; ++i) field1.vec_d[i] = expression(field1.vec_d[i], field2.vec_d[i], missval1);
     }
 
-  field1.nmiss = field_num_miss(field1);
+  field1.numMissVals = field_num_miss(field1);
 }
 
-class ModuleWct
+class Wct : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Wct",
+    .operators = { { "wct", WctHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Wct> registration = RegisterEntry<Wct>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -84,11 +96,8 @@ class ModuleWct
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("wct", 0, 0, nullptr);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -147,11 +156,11 @@ public:
           {
             int varID1, levelID1;
             cdo_inq_record(streamID1, &varID1, &levelID1);
-            cdo_read_record(streamID1, field1.vec_d.data(), &field1.nmiss);
+            cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
 
             int varID2, levelID2;
             cdo_inq_record(streamID2, &varID2, &levelID2);
-            cdo_read_record(streamID2, field2.vec_d.data(), &field2.nmiss);
+            cdo_read_record(streamID2, field2.vec_d.data(), &field2.numMissVals);
 
             if (varID1 != varID2 || levelID1 != levelID2) cdo_abort("Input streams have different structure!");
 
@@ -163,7 +172,7 @@ public:
             farexpr(field1, field2, windchillTemperature);
 
             cdo_def_record(streamID3, varID3, levelID1);
-            cdo_write_record(streamID3, field1.vec_d.data(), field1.nmiss);
+            cdo_write_record(streamID3, field1.vec_d.data(), field1.numMissVals);
           }
 
         tsID++;
@@ -176,18 +185,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Wct(void *process)
-{
-  ModuleWct wct;
-  wct.init(process);
-  wct.run();
-  wct.close();
-
-  return nullptr;
-}
diff --git a/src/Wind.cc b/src/Wind.cc
index 40b2552db46166714291b06bf2b847b804cbe931..0b62441ae8d4036792be08c2db14377df72aae5f 100644
--- a/src/Wind.cc
+++ b/src/Wind.cc
@@ -66,8 +66,25 @@ defineAttributesPS(int vlistID2, int varID1, int varID2)
   cdiDefKeyString(vlistID2, varID2, CDI_KEY_UNITS, "m^2/s");
 }
 
-class ModuleWind
+class Wind : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Wind",
+    .operators = { { "uv2dv", WindHelp },
+                   { "uv2dvl", WindHelp },
+                   { "dv2uv", WindHelp },
+                   { "dv2uvl", WindHelp },
+                   { "dv2ps", Wind2Help } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Wind> registration = RegisterEntry<Wind>(module);
+
+  int UV2DV, UV2DVL, DV2UV, DV2UVL, DV2PS;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -86,15 +103,9 @@ class ModuleWind
 
   int operatorID;
 
-  int UV2DV;
-  int UV2DVL;
-  int DV2UV;
-  int DV2UVL;
-  int DV2PS;
-
   size_t nlev = 0;
   int gridID1 = -1, gridID2 = -1;
-  size_t nmiss;
+  size_t numMissVals;
   long ntr = -1;
   int varID1 = -1, varID2 = -1;
   SP_Transformation spTrans;
@@ -102,24 +113,21 @@ class ModuleWind
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-    UV2DV  = cdo_operator_add("uv2dv",  0, 0, nullptr);
-    UV2DVL = cdo_operator_add("uv2dvl", 0, 0, nullptr);
-    DV2UV  = cdo_operator_add("dv2uv",  0, 0, nullptr);
-    DV2UVL = cdo_operator_add("dv2uvl", 0, 0, nullptr);
-    DV2PS  = cdo_operator_add("dv2ps",  0, 0, nullptr);
+    UV2DV = module.get_id("uv2dv");
+    UV2DVL = module.get_id("uv2dvl");
+    DV2UV = module.get_id("dv2uv");
+    DV2UVL = module.get_id("dv2uvl");
+    DV2PS = module.get_id("dv2ps");
 
     operatorID = cdo_operator_id();
 
-     luv2dv = (operatorID == UV2DV || operatorID == UV2DVL);
-     ldv2uv = (operatorID == DV2UV || operatorID == DV2UVL);
-     linear = (operatorID == UV2DVL || operatorID == DV2UVL);
+    luv2dv = (operatorID == UV2DV || operatorID == UV2DVL);
+    ldv2uv = (operatorID == DV2UV || operatorID == DV2UVL);
+    linear = (operatorID == UV2DVL || operatorID == DV2UVL);
 
     int (*nlat2ntr)(int) = linear ? nlat_to_ntr_linear : nlat_to_ntr;
     const char *ctype = linear ? "l" : "";
@@ -127,12 +135,13 @@ public:
     if ((luv2dv || ldv2uv) && cdo_operator_argc() == 1)
       {
         std::string type = parameter_to_word(cdo_operator_argv(0));
+        // clang-format off
         if      (type == "linear")    { nlat2ntr = nlat_to_ntr_linear; ctype = "l"; }
         else if (type == "cubic")     { nlat2ntr = nlat_to_ntr_cubic; ctype = "c"; }
         else if (type == "quadratic") { nlat2ntr = nlat_to_ntr; }
         else cdo_abort("Unsupported type: %s\n", type);
+        // clang-format on
       }
-    // clang-format on
 
     streamID1 = cdo_open_read(0);
 
@@ -202,9 +211,9 @@ public:
             if (gridID1 != vlistInqVarGrid(vlistID1, varID2)) cdo_abort("U and V wind must have the same grid represention!");
 
             auto numLPE = gridInqNP(gridID1);
-            const long nlon = gridInqXsize(gridID1);
-            const long nlat = gridInqYsize(gridID1);
-            const long ntr1 = nlat2ntr(nlat);
+            long nlon = gridInqXsize(gridID1);
+            long nlat = gridInqYsize(gridID1);
+            long ntr1 = nlat2ntr(nlat);
 
             if (numLPE > 0 && nlat != (numLPE * 2)) cdo_abort("U and V fields on Gaussian grid are not global!");
 
@@ -246,8 +255,8 @@ public:
 
             if (gridIDgp != -1)
               {
-                const long nlat = gridInqYsize(gridIDgp);
-                const long ntr1 = nlat2ntr(nlat);
+                long nlat = gridInqYsize(gridIDgp);
+                long ntr1 = nlat2ntr(nlat);
                 if (gridInqTrunc(gridIDsp) != ntr1) gridIDgp = -1;
               }
 
@@ -262,8 +271,8 @@ public:
 
             defineAttributesUV(vlistID2, gridID2, varID1, varID2);
 
-            const long nlon = gridInqXsize(gridID2);
-            const long nlat = gridInqYsize(gridID2);
+            long nlon = gridInqXsize(gridID2);
+            long nlat = gridInqYsize(gridID2);
             ntr = gridInqTrunc(gridID1);
             nlev = zaxisInqSize(vlistInqVarZaxis(vlistID1, varID1));
 
@@ -311,6 +320,7 @@ public:
         ovar2.resize(nlev * gridsize);
       }
   }
+
   void
   run()
   {
@@ -330,8 +340,8 @@ public:
 
             if ((varID1 != -1 && varID2 != -1) && (varID == varID1 || varID == varID2))
               {
-                cdo_read_record(streamID1, array1.data(), &nmiss);
-                if (nmiss) cdo_abort("Missing values unsupported for spectral data!");
+                cdo_read_record(streamID1, array1.data(), &numMissVals);
+                if (numMissVals) cdo_abort("Missing values unsupported for spectral data!");
 
                 auto gridsize = gridInqSize(gridID1);
                 auto offset = gridsize * levelID;
@@ -347,8 +357,8 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    cdo_read_record(streamID1, array1.data(), &nmiss);
-                    cdo_write_record(streamID2, array1.data(), nmiss);
+                    cdo_read_record(streamID1, array1.data(), &numMissVals);
+                    cdo_write_record(streamID2, array1.data(), numMissVals);
                   }
               }
           }
@@ -407,17 +417,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-void *
-Wind(void *process)
-{
-  ModuleWind wind;
-  wind.init(process);
-  wind.run();
-  wind.close();
-
-  return nullptr;
-}
diff --git a/src/WindTrans.cc b/src/WindTrans.cc
index e96344a44f974e9bedd7852eb38913d5993ebe4b..7808239d5445026ebd072eb8416ea277ba4f30a4 100644
--- a/src/WindTrans.cc
+++ b/src/WindTrans.cc
@@ -564,8 +564,8 @@ DestaggerUV()
 
               if (UorV >= 0)  // re-check again since it could mean that current record with U or V is not staggered
                 {
-                  size_t nmiss;
-                  cdo_read_record(streamID1, ivar.data(), &nmiss);
+                  size_t numMissVals;
+                  cdo_read_record(streamID1, ivar.data(), &numMissVals);
 
                   // void destaggerUorV(double *fu, double *fuOut, int klev, int nlat, int nlon, int UorV, long int offset);
                   // We handle one level at the time; klev=1;offset=0.
@@ -595,7 +595,7 @@ DestaggerUV()
                     cdo_print("Setting GRID id from: %d => to: %d", var.gridID, vlistInqVarGrid(vlistID2, varID));
 
                   cdo_def_record(streamID2, varID, levelID);
-                  cdo_write_record(streamID2, ovar.data(), nmiss);
+                  cdo_write_record(streamID2, ovar.data(), numMissVals);
                 }
               else
                 {  // copy the record to the output unchanged...
@@ -625,8 +625,6 @@ DestaggerUV()
   cdo_stream_close(streamID2);
   cdo_stream_close(streamID1);
 
-  cdo_finish();
-
   return 0;
 }
 
@@ -1106,401 +1104,414 @@ project_uv_latlon(int gridID, double *us, double *vs)
   Debug(cdoDebugExt >= 20, "%s(gridname=%s) finished.", __func__, gridNamePtr(gridInqType(gridID)));
 }
 
-
-class ModuleWindtrans
+class WindTrans : public Process
 {
-  int operatorID;
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "WindTrans",
+    .operators = { { "uvDestag", WindTransHelp },
+                   { "rotuvN", WindTransHelp },
+                   { "rotuvNorth", WindTransHelp },
+                   { "projuvLatLon", WindTransHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<WindTrans> registration = RegisterEntry<WindTrans>(module);
   int UVDESTAG, ROTUVNORTH, ROTUVN, PROJUVLATLON;
+  int operatorID;
 
-void *
-TransformUV()
-{
-  int varID, levelID;
-  int varID1, varID2, nlevel1, nlevel2;
-  size_t gridsize = 0;
-  int code, gridID;
-  int ltype, level, nlevs, zaxisID;
-  int pnum, pcat, pdis;
-  int chcodes[MAXARG];
-  const char *chvars[MAXARG];
-  int gridIDcurvl = -1;
-  int gridIDlastused = -1;
-
-  // Note: Already initialized by the caller! Don't call again: cdo_initialize(process);
-
-  operator_input_arg("Pairs of u and v in the rotated system;\n usage: rotuvNorth,u,v  -or- rotuvNorth,33,34");
-
-  int nch = cdo_operator_argc();
-  if (nch != 2) cdo_abort("Number of input arguments != 2");
-  if (nch >= MAXARG) cdo_abort("Number of input arguments >= %d", MAXARG);
+  void *
+  TransformUV()
+  {
+    int varID, levelID;
+    int varID1, varID2, nlevel1, nlevel2;
+    size_t gridsize = 0;
+    int code, gridID;
+    int ltype, level, nlevs, zaxisID;
+    int pnum, pcat, pdis;
+    int chcodes[MAXARG];
+    const char *chvars[MAXARG];
+    int gridIDcurvl = -1;
+    int gridIDlastused = -1;
+
+    // Note: Already initialized by the caller! Don't call again: cdo_initialize(process);
+
+    operator_input_arg("Pairs of u and v in the rotated system;\n usage: rotuvNorth,u,v  -or- rotuvNorth,33,34");
+
+    int nch = cdo_operator_argc();
+    if (nch != 2) cdo_abort("Number of input arguments != 2");
+    if (nch >= MAXARG) cdo_abort("Number of input arguments >= %d", MAXARG);
+
+    bool lvar = false;  // We have a list of codes
+    int len = (int) cdo_operator_argv(0).size();
+    int ix = (cdo_operator_argv(0)[0] == '-') ? 1 : 0;
+    for (int i = ix; i < len; ++i)
+      if (!std::isdigit(cdo_operator_argv(0)[i]))
+        {
+          lvar = true;  // We have a list of variables
+          break;
+        }
 
-  bool lvar = false;  // We have a list of codes
-  int len = (int) cdo_operator_argv(0).size();
-  int ix = (cdo_operator_argv(0)[0] == '-') ? 1 : 0;
-  for (int i = ix; i < len; ++i)
-    if (!std::isdigit(cdo_operator_argv(0)[i]))
+    if (lvar)
       {
-        lvar = true;  // We have a list of variables
-        break;
+        for (int i = 0; i < nch; ++i) chvars[i] = cdo_operator_argv(i).c_str();
+      }
+    else
+      {
+        for (int i = 0; i < nch; ++i) chcodes[i] = parameter_to_int(cdo_operator_argv(i));
       }
 
-  if (lvar)
-    {
-      for (int i = 0; i < nch; ++i) chvars[i] = cdo_operator_argv(i).c_str();
-    }
-  else
-    {
-      for (int i = 0; i < nch; ++i) chcodes[i] = parameter_to_int(cdo_operator_argv(i));
-    }
-
-  auto streamID1 = cdo_open_read(0);
+    auto streamID1 = cdo_open_read(0);
 
-  auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-  auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
 
-  auto nvars = vlistNvars(vlistID1);
-  auto num_recs = vlistNrecs(vlistID1);
+    auto nvars = vlistNvars(vlistID1);
+    auto num_recs = vlistNrecs(vlistID1);
 
-  std::vector<RecordInfo> recList(num_recs);
+    std::vector<RecordInfo> recList(num_recs);
 
-  std::vector<std::vector<size_t>> varnmiss(nvars);
-  Varray3D<double> vardata(nvars);
+    std::vector<std::vector<size_t>> varnumMissVals(nvars);
+    Varray3D<double> vardata(nvars);
 
-  // U & V are NOT grid relative
-  for (varID = 0; varID < nvars; ++varID) cdiDefKeyInt(vlistID2, varID, CDI_KEY_UVRELATIVETOGRID, 0);
+    // U & V are NOT grid relative
+    for (varID = 0; varID < nvars; ++varID) cdiDefKeyInt(vlistID2, varID, CDI_KEY_UVRELATIVETOGRID, 0);
 
-  VarList varList1, varList2;
-  varList_init(varList1, vlistID1);
-  varList_init(varList2, vlistID2);
+    VarList varList1, varList2;
+    varList_init(varList1, vlistID1);
+    varList_init(varList2, vlistID2);
 
-  bool lfound[MAXARG];
-  for (int i = 0; i < nch; ++i) lfound[i] = false;
+    bool lfound[MAXARG];
+    for (int i = 0; i < nch; ++i) lfound[i] = false;
 
-  if (lvar)
-    {
-      for (varID = 0; varID < nvars; ++varID)
-        {
-          for (int i = 0; i < nch; ++i)
-            if (varList2[varID].name == chvars[i]) lfound[i] = true;
-        }
-      for (int i = 0; i < nch; ++i)
-        if (!lfound[i]) cdo_abort("Variable %s not found!", chvars[i]);
-    }
-  else
-    {
-      for (varID = 0; varID < nvars; ++varID)
-        {
-          for (int i = 0; i < nch; ++i)
-            if (varList2[varID].code == chcodes[i]) lfound[i] = true;
-        }
-      for (int i = 0; i < nch; ++i)
-        if (!lfound[i]) cdo_abort("Code %d not found!", chcodes[i]);
-    }
-
-  int VarIsU, VarIsV;
-
-  // NOTE: Variable with codes (3[3,4],105,10) will get from CDO typically a name: "10u", resp. "10v"
-
-  for (varID = 0; varID < nvars; ++varID)
-    {
-      const auto &var = varList1[varID];
+    if (lvar)
+      {
+        for (varID = 0; varID < nvars; ++varID)
+          {
+            for (int i = 0; i < nch; ++i)
+              if (varList2[varID].name == chvars[i]) lfound[i] = true;
+          }
+        for (int i = 0; i < nch; ++i)
+          if (!lfound[i]) cdo_abort("Variable %s not found!", chvars[i]);
+      }
+    else
+      {
+        for (varID = 0; varID < nvars; ++varID)
+          {
+            for (int i = 0; i < nch; ++i)
+              if (varList2[varID].code == chcodes[i]) lfound[i] = true;
+          }
+        for (int i = 0; i < nch; ++i)
+          if (!lfound[i]) cdo_abort("Code %d not found!", chcodes[i]);
+      }
 
-      cdiDecodeParam(var.param, &pnum, &pcat, &pdis);
-      code = pnum;
-      zaxisID = var.zaxisID;
-      ltype = zaxis_to_ltype(zaxisID);
-      nlevs = zaxisInqSize(zaxisID);
+    int VarIsU, VarIsV;
 
-      gridID = var.gridID;
-      if (cdoDebugExt >= 20)
-        cdo_print(
-            "Var.id [%4d] with grib code:%3d and has name: %6s; level type: %3d; number of levels: %3d; gridID: %d; zaxisID: %d",
-            varID, code, var.name, ltype, nlevs, gridID, zaxisID);
+    // NOTE: Variable with codes (3[3,4],105,10) will get from CDO typically a name: "10u", resp. "10v"
 
-      if (!(gridInqType(gridID) == GRID_PROJECTION && gridInqProjType(gridID) == CDI_PROJ_RLL))
-        cdo_abort("Only rotated lon/lat grids supported!");
+    for (varID = 0; varID < nvars; ++varID)
+      {
+        const auto &var = varList1[varID];
 
-      CheckVarIsU(varID, var.name, code);
-      CheckVarIsV(varID, var.name, code);
-      if (VarIsU || VarIsV)
-        {
-          gridsize = gridInqSize(gridID);
-          if (cdoDebugExt)
-            cdo_print("Allocating memory for variableID %4d (code=%3d): gridsize(%zu)*nlevels(%d) = %zu [%4.3f MB]", varID,
-                      varList2[varID].code, gridsize, nlevs, gridsize * nlevs, gridsize * nlevs * sizeof(double) / (1024.0 * 1024));
-          varnmiss[varID].resize(nlevs);
-          vardata[varID].resize(nlevs);
-          for (levelID = 0; levelID < nlevs; ++levelID) vardata[varID][levelID].resize(gridsize);
-        }
-    }
+        cdiDecodeParam(var.param, &pnum, &pcat, &pdis);
+        code = pnum;
+        zaxisID = var.zaxisID;
+        ltype = zaxis_to_ltype(zaxisID);
+        nlevs = zaxisInqSize(zaxisID);
 
-  Debug(cdoDebugExt, "Neccessary memory has been allocated.");
+        gridID = var.gridID;
+        if (cdoDebugExt >= 20)
+          cdo_print(
+              "Var.id [%4d] with grib code:%3d and has name: %6s; level type: %3d; number of levels: %3d; gridID: %d; zaxisID: %d",
+              varID, code, var.name, ltype, nlevs, gridID, zaxisID);
 
-  auto taxisID1 = vlistInqTaxis(vlistID1);
-  auto taxisID2 = taxisDuplicate(taxisID1);
-  vlistDefTaxis(vlistID2, taxisID2);
+        if (!(gridInqType(gridID) == GRID_PROJECTION && gridInqProjType(gridID) == CDI_PROJ_RLL))
+          cdo_abort("Only rotated lon/lat grids supported!");
 
-  auto streamID2 = cdo_open_write(1);
+        CheckVarIsU(varID, var.name, code);
+        CheckVarIsV(varID, var.name, code);
+        if (VarIsU || VarIsV)
+          {
+            gridsize = gridInqSize(gridID);
+            if (cdoDebugExt)
+              cdo_print("Allocating memory for variableID %4d (code=%3d): gridsize(%zu)*nlevels(%d) = %zu [%4.3f MB]", varID,
+                        varList2[varID].code, gridsize, nlevs, gridsize * nlevs,
+                        gridsize * nlevs * sizeof(double) / (1024.0 * 1024));
+            varnumMissVals[varID].resize(nlevs);
+            vardata[varID].resize(nlevs);
+            for (levelID = 0; levelID < nlevs; ++levelID) vardata[varID][levelID].resize(gridsize);
+          }
+      }
 
-  cdo_def_vlist(streamID2, vlistID2);          // from this point the stream is using a different vlistID !!!!!
-  vlistID2 = cdo_stream_inq_vlist(streamID2);  // refresh it
+    Debug(cdoDebugExt, "Neccessary memory has been allocated.");
 
-  int tsID = 0;
-  while (true)
-    {
-      auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
-      if (nrecs == 0) break;
+    auto taxisID1 = vlistInqTaxis(vlistID1);
+    auto taxisID2 = taxisDuplicate(taxisID1);
+    vlistDefTaxis(vlistID2, taxisID2);
 
-      cdo_taxis_copy_timestep(taxisID2, taxisID1);
-      cdo_def_timestep(streamID2, tsID);
+    auto streamID2 = cdo_open_write(1);
 
-      if (cdoDebugExt) cdo_print("About to read U & V data to memory. Other data will be stream-copied to the output file.");
+    cdo_def_vlist(streamID2, vlistID2);          // from this point the stream is using a different vlistID !!!!!
+    vlistID2 = cdo_stream_inq_vlist(streamID2);  // refresh it
 
-      for (int recID = 0; recID < nrecs; ++recID)
-        {
-          cdo_inq_record(streamID1, &varID, &levelID);
-          code = varList1[varID].code;
-          zaxisID = varList1[varID].zaxisID;
-          ltype = zaxis_to_ltype(zaxisID);
-          level = zaxisInqLevel(zaxisID, levelID);
-
-          if (vardata[varID].empty())
-            {                             // This means that it is not eighter U neither V.
-              recList[recID].varID = -1;  // We will NOT record/store this field in memory
-              recList[recID].levelID = -1;
-              // We will stream-copy this data
-              cdo_def_record(streamID2, varID, levelID);
-              // if ( cdoDebugExt>10 ) cdo_print("Copying data record.. %05d (timestep:%05d)", recID, tsID);
-              if (cdoDebugExt >= 20)
-                cdo_print("Stream-copy data record:    %05d (timestep:%d); Var.id [%4d]; (code=%3d; ltype=%3d; level=%4d; "
-                          "levelID=%3d)",
-                          recID, tsID, varID, code, ltype, level, levelID);
-              cdo_copy_record(streamID2, streamID1);  // cannot do this ! We have to set the flag uvGridRelative = 0
-            }
-          else
-            {
-              recList[recID].varID = varID;
-              recList[recID].levelID = levelID;
-              if (cdoDebugExt >= 10)
-                cdo_print("Memmory-read data record:   %05d (timestep:%d); Var.id [%4d]; (code=%3d; ltype=%3d; level=%4d; "
-                          "levelID=%3d)",
-                          recID, tsID, varID, code, ltype, level, levelID);
-              cdo_read_record(streamID1, vardata[varID][levelID].data(), &varnmiss[varID][levelID]);
-              if (varnmiss[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
-            }
-        }  // end of for ( recID = 0; recID < nrecs; ..
+    int tsID = 0;
+    while (true)
+      {
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        if (nrecs == 0) break;
 
-      Debug(cdoDebugExt, "All neccessary U & V data are in memory. About to transform the windvectors...");
+        cdo_taxis_copy_timestep(taxisID2, taxisID1);
+        cdo_def_timestep(streamID2, tsID);
 
-      int code1, zaxisID1, ltype1;
-      int code2, zaxisID2, ltype2;
-      Debug(cdoDebugExt, "Looping over %d variables to look for U-wind..", nvars);
+        if (cdoDebugExt) cdo_print("About to read U & V data to memory. Other data will be stream-copied to the output file.");
 
-      // find u-variables:
-      for (varID1 = 0; varID1 < nvars; ++varID1)
-        if (vardata[varID1].empty())
+        for (int recID = 0; recID < nrecs; ++recID)
           {
-            // if ( cdoDebugExt ) cdo_print("Checking U-wind: vardata[%d]== nullptr ",varID1);
-          }
-        else  // This means that it is U or V.
-          {
-            code1 = varList2[varID1].code;
-            zaxisID1 = varList2[varID1].zaxisID;
-            ltype1 = zaxis_to_ltype(zaxisID1);
-            nlevel1 = zaxisInqSize(zaxisID1);
-            CheckVarIsV(varID1, varList2[varID1].name, code1);
-            if (VarIsV) continue;
-            if (cdoDebugExt >= 20)
-              cdo_print("Checking U-wind: Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of levels: %3d; "
-                        "zaxisID: %d",
-                        varID1, code1, varList2[varID1].name, ltype1, nlevel1, zaxisID1);
-            CheckVarIsU(varID1, varList2[varID1].name, code1);
-            if (!VarIsU) continue;
-            if (cdoDebugExt >= 10)
-              cdo_print("** FOUND U-wind; Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of levels: %3d; "
-                        "zaxisID: %d",
-                        varID1, code1, varList2[varID1].name, ltype1, nlevel1, zaxisID1);
-            auto usvarID = varID1;
-            // find corresponding v-variable to u-variable:
-            for (varID2 = 0; varID2 < nvars; ++varID2)
-              if (vardata[varID2].empty())
-                {
-                  // if ( cdoDebugExt ) cdo_print("Checking V-wind: vardata[%d]== nullptr ",varID1);
-                }
-              else  // This means that it is U or V.
-                {
-                  code2 = varList2[varID2].code;
-                  zaxisID2 = varList2[varID2].zaxisID;
-                  ltype2 = zaxis_to_ltype(zaxisID2);
-                  nlevel2 = zaxisInqSize(zaxisID2);
-                  CheckVarIsU(varID2, varList2[varID2].name, code2);
-                  if (VarIsU) continue;
-                  if (cdoDebugExt >= 20)
-                    cdo_print("Checking V-wind: Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of "
-                              "levels: %3d; zaxisID: %d",
-                              varID2, code2, varList2[varID2].name, ltype2, nlevel2, zaxisID2);
-                  CheckVarIsV(varID2, varList2[varID2].name, code2);
-                  if (!VarIsV) continue;
-                  if (!((ltype1 == ltype2) && (nlevel1 == nlevel2) && (zaxisID1 == zaxisID2))) continue;
-                  if (cdoDebugExt >= 10)
-                    cdo_print("** FOUND V-wind; Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of "
-                              "levels: %3d; zaxisID: %d",
-                              varID2, code2, varList2[varID2].name, ltype1, nlevel2, zaxisID2);
-                  auto vsvarID = varID2;
-                  if (cdoDebugExt >= 20)
-                    cdo_print("Using code %d [%d](u) and code %d [%d](v)", varList1[varID1].code, code1, varList1[varID2].code,
-                              code2);
-                  gridID = varList1[varID1].gridID;
-                  if (operatorID != ROTUVN)  // ROTUVN operator does not need creation of gridIDcurvl ...
-                    {
-                      if ((gridIDcurvl != -1) && (gridIDlastused != gridID))
-                        cdo_abort("The gridID (%d) used just previously for uv-wind tranformation is not same this "
-                                  "time(%d)!",
-                                  gridIDlastused, gridID);
+            cdo_inq_record(streamID1, &varID, &levelID);
+            code = varList1[varID].code;
+            zaxisID = varList1[varID].zaxisID;
+            ltype = zaxis_to_ltype(zaxisID);
+            level = zaxisInqLevel(zaxisID, levelID);
+
+            if (vardata[varID].empty())
+              {                             // This means that it is not eighter U neither V.
+                recList[recID].varID = -1;  // We will NOT record/store this field in memory
+                recList[recID].levelID = -1;
+                // We will stream-copy this data
+                cdo_def_record(streamID2, varID, levelID);
+                // if ( cdoDebugExt>10 ) cdo_print("Copying data record.. %05d (timestep:%05d)", recID, tsID);
+                if (cdoDebugExt >= 20)
+                  cdo_print("Stream-copy data record:    %05d (timestep:%d); Var.id [%4d]; (code=%3d; ltype=%3d; level=%4d; "
+                            "levelID=%3d)",
+                            recID, tsID, varID, code, ltype, level, levelID);
+                cdo_copy_record(streamID2, streamID1);  // cannot do this ! We have to set the flag uvGridRelative = 0
+              }
+            else
+              {
+                recList[recID].varID = varID;
+                recList[recID].levelID = levelID;
+                if (cdoDebugExt >= 10)
+                  cdo_print("Memmory-read data record:   %05d (timestep:%d); Var.id [%4d]; (code=%3d; ltype=%3d; level=%4d; "
+                            "levelID=%3d)",
+                            recID, tsID, varID, code, ltype, level, levelID);
+                cdo_read_record(streamID1, vardata[varID][levelID].data(), &varnumMissVals[varID][levelID]);
+                if (varnumMissVals[varID][levelID]) cdo_abort("Missing values unsupported for this operator!");
+              }
+          }  // end of for ( recID = 0; recID < nrecs; ..
 
-                      if (gridIDcurvl == -1)
-                        {
-                          if (cdoDebugExt) cdo_print("Building LAT-LON grid for the direction to the North. (First time only).");
-                          gridIDlastused = gridID;
-                          // Compute 2D array with latlons only once. We expect that all horizontal grids for UV are same.
-                          // NOTE: At this stage U and V cannot be staggered!
-                          gridIDcurvl = gridToCurvilinear(gridID, NeedCorners::Yes);
-                          if (cdoDebugExt)
-                            cdo_print("Transformed rotated-latLon grid (id:%d) to curvilinear (id:%d) with true lat-lon "
-                                      "coordinates.",
-                                      gridID, gridIDcurvl);
-                          // Grid definition with id: "gridIDcurvl" contains latlons of every gridpoint..
-                          // For details see: ./libcdi/src/cdi.h; Setgridtype to GRID_CURVILINEAR
-
-                          if (gridIDcurvl == -1) cdo_abort("Creation of curvilinear grid definition failed!");
-
-                          if (gridInqType(gridIDcurvl) != GRID_CURVILINEAR)
-                            {
-                              gridDestroy(gridIDcurvl);
-                              cdo_abort("Creation of curvilinear grid definition failed: type != GRID_CURVILINEAR");
-                            }
-                          if (cdoDebugExt)
-                            {
-                              double xpole = 0, ypole = 0, angle = 0;
-                              if (gridInqType(gridID) == GRID_PROJECTION && gridInqProjType(gridID) == CDI_PROJ_RLL)
-                                gridInqParamRLL(gridID, &xpole, &ypole, &angle);
-
-                              cdo_print("GRID_PROJECTION(id: %d) && CDI_PROJ_RLL:", gridID);
-                              cdo_print("grid Xsize   %zu, grid Ysize   %zu", gridInqXsize(gridID), gridInqYsize(gridID));
-                              cdo_print("grid Xfirst  %4.3f, grid Yfirst  %4.3f", gridInqXval(gridID, 0), gridInqYval(gridID, 0));
-                              cdo_print("grid Xinc   %4.3f, grid Yinc   %4.3f", gridInqXinc(gridID), gridInqYinc(gridID));
-                              cdo_print("grid Xpole   %4.3f, grid Ypole   %4.3f", xpole, ypole);
-                              cdo_print("GRID_CURVILINEAR (id: %d):", gridIDcurvl);
-                              cdo_print("grid Xsize   %zu, grid Ysize   %zu", gridInqXsize(gridIDcurvl), gridInqYsize(gridIDcurvl));
-                              cdo_print("grid Xfirst  %4.3f, grid Yfirst  %4.3f", gridInqXval(gridIDcurvl, 0),
-                                        gridInqYval(gridIDcurvl, 0));
-                              cdo_print("grid Xlast   %4.3f, grid Ylast   %4.3f",
-                                        gridInqXval(gridIDcurvl, gridInqSize(gridIDcurvl) - 1),
-                                        gridInqYval(gridIDcurvl, gridInqSize(gridIDcurvl) - 1));
-                              if (cdoDebugExt >= 20)
-                                {
-                                  printf("Xvals (size=%zu):\n", gridInqSize(gridIDcurvl));
-                                  size_t ii;
-                                  for (ii = 0; ii < 10; ++ii) printf("%4.3f ", gridInqXval(gridIDcurvl, ii));
-                                  printf("\n...\n");
-                                  for (ii = gridInqSize(gridIDcurvl) - 10; ii < gridInqSize(gridIDcurvl); ++ii)
-                                    printf("%4.3f ", gridInqXval(gridIDcurvl, ii));
-                                  printf("\n");
-                                  printf("Yvals (size=%zu):\n", gridInqSize(gridIDcurvl));
-                                  for (ii = 0; ii < 10; ++ii) printf("%4.3f ", gridInqYval(gridIDcurvl, ii));
-                                  printf("\n...\n");
-                                  for (ii = gridInqSize(gridIDcurvl) - 10; ii < gridInqSize(gridIDcurvl); ++ii)
-                                    printf("%4.3f ", gridInqYval(gridIDcurvl, ii));
-                                  printf("\n");
-                                }
-                            }  // end of if (cdoDebugExt)
-                          Debug(cdoDebugExt, "LAT-LON grid created.");
-                        }  // end of if (gridIDcurvl==-1)
-                    }      // end of if (operatorID != ROTUVN)
-
-                  int uRelativeToGrid = -1, vRelativeToGrid = -1;
-                  cdiInqKeyInt(vlistID1, varID1, CDI_KEY_UVRELATIVETOGRID, &uRelativeToGrid);
-                  cdiInqKeyInt(vlistID1, varID2, CDI_KEY_UVRELATIVETOGRID, &vRelativeToGrid);
-
-                  if (uRelativeToGrid != 1)
-                    {
-                      cdo_warning("U component is NOT relative to grid. No transformation to north-pole takes place!");
-                    }
-                  else if (vRelativeToGrid != 1)
-                    {
-                      cdo_warning("V component is NOT relative to grid. No transformation to north-pole takes place!");
-                    }
-                  else
-                    {
-                      for (levelID = 0; levelID < nlevel1; ++levelID)
-                        {
-                          if (cdoDebugExt)
-                            cdo_print("RotuvNorth(): processing  level type: %d; level %d (out of [0:%d])", ltype1, levelID,
-                                      nlevel1 - 1);
-                          auto usvar = vardata[usvarID][levelID].data();
-                          auto vsvar = vardata[vsvarID][levelID].data();
-                          if (operatorID == ROTUVNORTH)
-                            {
-                              rot_uv_north(gridIDcurvl, usvar, vsvar);
-                              // rot_uv_north(gridIDlastused, usvar, vsvar);
-                              // transform "in-place" the uv from grid relative into north-pole related
-                            }
-                          else if (operatorID == PROJUVLATLON)
-                            project_uv_latlon(gridIDcurvl, usvar, vsvar);
-                          else if (operatorID == ROTUVN)
-                            rot_uv_back_mode64(gridID, usvar, vsvar);
-                        }
-                    }
+        Debug(cdoDebugExt, "All neccessary U & V data are in memory. About to transform the windvectors...");
 
-                  Debug(cdoDebugExt, "Finished processing level type: %d", ltype1);
-                  break;
-                }  // end  for varID2
-          }        // end  for varID1
+        int code1, zaxisID1, ltype1;
+        int code2, zaxisID2, ltype2;
+        Debug(cdoDebugExt, "Looping over %d variables to look for U-wind..", nvars);
 
-      for (int recID = 0; recID < nrecs; ++recID)
-        {
-          varID = recList[recID].varID;
-          levelID = recList[recID].levelID;
-          if (varID != -1)
+        // find u-variables:
+        for (varID1 = 0; varID1 < nvars; ++varID1)
+          if (vardata[varID1].empty())
             {
-              code = varList1[varID].code;
-              zaxisID = varList1[varID].zaxisID;
-              ltype = zaxis_to_ltype(zaxisID);
-              level = zaxisInqLevel(zaxisID, levelID);
-              if (cdoDebugExt >= 10)
-                cdo_print("Write modified data record: %05d (timestep:%d); Var.id [%4d]; (code=%3d; ltype=%3d; level=%4d; "
-                          "levelID=%3d)",
-                          recID, tsID, varID, code, ltype, level, levelID);
-
-              cdo_def_record(streamID2, varID, levelID);
-              cdo_write_record(streamID2, vardata[varID][levelID].data(), varnmiss[varID][levelID]);
+              // if ( cdoDebugExt ) cdo_print("Checking U-wind: vardata[%d]== nullptr ",varID1);
             }
-        }
+          else  // This means that it is U or V.
+            {
+              code1 = varList2[varID1].code;
+              zaxisID1 = varList2[varID1].zaxisID;
+              ltype1 = zaxis_to_ltype(zaxisID1);
+              nlevel1 = zaxisInqSize(zaxisID1);
+              CheckVarIsV(varID1, varList2[varID1].name, code1);
+              if (VarIsV) continue;
+              if (cdoDebugExt >= 20)
+                cdo_print("Checking U-wind: Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of levels: %3d; "
+                          "zaxisID: %d",
+                          varID1, code1, varList2[varID1].name, ltype1, nlevel1, zaxisID1);
+              CheckVarIsU(varID1, varList2[varID1].name, code1);
+              if (!VarIsU) continue;
+              if (cdoDebugExt >= 10)
+                cdo_print("** FOUND U-wind; Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of levels: %3d; "
+                          "zaxisID: %d",
+                          varID1, code1, varList2[varID1].name, ltype1, nlevel1, zaxisID1);
+              auto usvarID = varID1;
+              // find corresponding v-variable to u-variable:
+              for (varID2 = 0; varID2 < nvars; ++varID2)
+                if (vardata[varID2].empty())
+                  {
+                    // if ( cdoDebugExt ) cdo_print("Checking V-wind: vardata[%d]== nullptr ",varID1);
+                  }
+                else  // This means that it is U or V.
+                  {
+                    code2 = varList2[varID2].code;
+                    zaxisID2 = varList2[varID2].zaxisID;
+                    ltype2 = zaxis_to_ltype(zaxisID2);
+                    nlevel2 = zaxisInqSize(zaxisID2);
+                    CheckVarIsU(varID2, varList2[varID2].name, code2);
+                    if (VarIsU) continue;
+                    if (cdoDebugExt >= 20)
+                      cdo_print("Checking V-wind: Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of "
+                                "levels: %3d; zaxisID: %d",
+                                varID2, code2, varList2[varID2].name, ltype2, nlevel2, zaxisID2);
+                    CheckVarIsV(varID2, varList2[varID2].name, code2);
+                    if (!VarIsV) continue;
+                    if (!((ltype1 == ltype2) && (nlevel1 == nlevel2) && (zaxisID1 == zaxisID2))) continue;
+                    if (cdoDebugExt >= 10)
+                      cdo_print("** FOUND V-wind; Var.id [%4d] with grib code:%3d; name: %6s; level type: %3d; number of "
+                                "levels: %3d; zaxisID: %d",
+                                varID2, code2, varList2[varID2].name, ltype1, nlevel2, zaxisID2);
+                    auto vsvarID = varID2;
+                    if (cdoDebugExt >= 20)
+                      cdo_print("Using code %d [%d](u) and code %d [%d](v)", varList1[varID1].code, code1, varList1[varID2].code,
+                                code2);
+                    gridID = varList1[varID1].gridID;
+                    if (operatorID != ROTUVN)  // ROTUVN operator does not need creation of gridIDcurvl ...
+                      {
+                        if ((gridIDcurvl != -1) && (gridIDlastused != gridID))
+                          cdo_abort("The gridID (%d) used just previously for uv-wind tranformation is not same this "
+                                    "time(%d)!",
+                                    gridIDlastused, gridID);
+
+                        if (gridIDcurvl == -1)
+                          {
+                            if (cdoDebugExt) cdo_print("Building LAT-LON grid for the direction to the North. (First time only).");
+                            gridIDlastused = gridID;
+                            // Compute 2D array with latlons only once. We expect that all horizontal grids for UV are same.
+                            // NOTE: At this stage U and V cannot be staggered!
+                            gridIDcurvl = gridToCurvilinear(gridID, NeedCorners::Yes);
+                            if (cdoDebugExt)
+                              cdo_print("Transformed rotated-latLon grid (id:%d) to curvilinear (id:%d) with true lat-lon "
+                                        "coordinates.",
+                                        gridID, gridIDcurvl);
+                            // Grid definition with id: "gridIDcurvl" contains latlons of every gridpoint..
+                            // For details see: ./libcdi/src/cdi.h; Setgridtype to GRID_CURVILINEAR
+
+                            if (gridIDcurvl == -1) cdo_abort("Creation of curvilinear grid definition failed!");
+
+                            if (gridInqType(gridIDcurvl) != GRID_CURVILINEAR)
+                              {
+                                gridDestroy(gridIDcurvl);
+                                cdo_abort("Creation of curvilinear grid definition failed: type != GRID_CURVILINEAR");
+                              }
+                            if (cdoDebugExt)
+                              {
+                                double xpole = 0, ypole = 0, angle = 0;
+                                if (gridInqType(gridID) == GRID_PROJECTION && gridInqProjType(gridID) == CDI_PROJ_RLL)
+                                  gridInqParamRLL(gridID, &xpole, &ypole, &angle);
+
+                                cdo_print("GRID_PROJECTION(id: %d) && CDI_PROJ_RLL:", gridID);
+                                cdo_print("grid Xsize   %zu, grid Ysize   %zu", gridInqXsize(gridID), gridInqYsize(gridID));
+                                cdo_print("grid Xfirst  %4.3f, grid Yfirst  %4.3f", gridInqXval(gridID, 0), gridInqYval(gridID, 0));
+                                cdo_print("grid Xinc   %4.3f, grid Yinc   %4.3f", gridInqXinc(gridID), gridInqYinc(gridID));
+                                cdo_print("grid Xpole   %4.3f, grid Ypole   %4.3f", xpole, ypole);
+                                cdo_print("GRID_CURVILINEAR (id: %d):", gridIDcurvl);
+                                cdo_print("grid Xsize   %zu, grid Ysize   %zu", gridInqXsize(gridIDcurvl),
+                                          gridInqYsize(gridIDcurvl));
+                                cdo_print("grid Xfirst  %4.3f, grid Yfirst  %4.3f", gridInqXval(gridIDcurvl, 0),
+                                          gridInqYval(gridIDcurvl, 0));
+                                cdo_print("grid Xlast   %4.3f, grid Ylast   %4.3f",
+                                          gridInqXval(gridIDcurvl, gridInqSize(gridIDcurvl) - 1),
+                                          gridInqYval(gridIDcurvl, gridInqSize(gridIDcurvl) - 1));
+                                if (cdoDebugExt >= 20)
+                                  {
+                                    printf("Xvals (size=%zu):\n", gridInqSize(gridIDcurvl));
+                                    size_t ii;
+                                    for (ii = 0; ii < 10; ++ii) printf("%4.3f ", gridInqXval(gridIDcurvl, ii));
+                                    printf("\n...\n");
+                                    for (ii = gridInqSize(gridIDcurvl) - 10; ii < gridInqSize(gridIDcurvl); ++ii)
+                                      printf("%4.3f ", gridInqXval(gridIDcurvl, ii));
+                                    printf("\n");
+                                    printf("Yvals (size=%zu):\n", gridInqSize(gridIDcurvl));
+                                    for (ii = 0; ii < 10; ++ii) printf("%4.3f ", gridInqYval(gridIDcurvl, ii));
+                                    printf("\n...\n");
+                                    for (ii = gridInqSize(gridIDcurvl) - 10; ii < gridInqSize(gridIDcurvl); ++ii)
+                                      printf("%4.3f ", gridInqYval(gridIDcurvl, ii));
+                                    printf("\n");
+                                  }
+                              }  // end of if (cdoDebugExt)
+                            Debug(cdoDebugExt, "LAT-LON grid created.");
+                          }  // end of if (gridIDcurvl==-1)
+                      }      // end of if (operatorID != ROTUVN)
+
+                    int uRelativeToGrid = -1, vRelativeToGrid = -1;
+                    cdiInqKeyInt(vlistID1, varID1, CDI_KEY_UVRELATIVETOGRID, &uRelativeToGrid);
+                    cdiInqKeyInt(vlistID1, varID2, CDI_KEY_UVRELATIVETOGRID, &vRelativeToGrid);
+
+                    if (uRelativeToGrid != 1)
+                      {
+                        cdo_warning("U component is NOT relative to grid. No transformation to north-pole takes place!");
+                      }
+                    else if (vRelativeToGrid != 1)
+                      {
+                        cdo_warning("V component is NOT relative to grid. No transformation to north-pole takes place!");
+                      }
+                    else
+                      {
+                        for (levelID = 0; levelID < nlevel1; ++levelID)
+                          {
+                            if (cdoDebugExt)
+                              cdo_print("RotuvNorth(): processing  level type: %d; level %d (out of [0:%d])", ltype1, levelID,
+                                        nlevel1 - 1);
+                            auto usvar = vardata[usvarID][levelID].data();
+                            auto vsvar = vardata[vsvarID][levelID].data();
+                            if (operatorID == ROTUVNORTH)
+                              {
+                                rot_uv_north(gridIDcurvl, usvar, vsvar);
+                                // rot_uv_north(gridIDlastused, usvar, vsvar);
+                                // transform "in-place" the uv from grid relative into north-pole related
+                              }
+                            else if (operatorID == PROJUVLATLON)
+                              project_uv_latlon(gridIDcurvl, usvar, vsvar);
+                            else if (operatorID == ROTUVN)
+                              rot_uv_back_mode64(gridID, usvar, vsvar);
+                          }
+                      }
+
+                    Debug(cdoDebugExt, "Finished processing level type: %d", ltype1);
+                    break;
+                  }  // end  for varID2
+            }        // end  for varID1
+
+        for (int recID = 0; recID < nrecs; ++recID)
+          {
+            varID = recList[recID].varID;
+            levelID = recList[recID].levelID;
+            if (varID != -1)
+              {
+                code = varList1[varID].code;
+                zaxisID = varList1[varID].zaxisID;
+                ltype = zaxis_to_ltype(zaxisID);
+                level = zaxisInqLevel(zaxisID, levelID);
+                if (cdoDebugExt >= 10)
+                  cdo_print("Write modified data record: %05d (timestep:%d); Var.id [%4d]; (code=%3d; ltype=%3d; level=%4d; "
+                            "levelID=%3d)",
+                            recID, tsID, varID, code, ltype, level, levelID);
+
+                cdo_def_record(streamID2, varID, levelID);
+                cdo_write_record(streamID2, vardata[varID][levelID].data(), varnumMissVals[varID][levelID]);
+              }
+          }
 
-      tsID++;
-    }  // end of while (true)
+        tsID++;
+      }  // end of while (true)
 
-  cdo_stream_close(streamID2);
-  cdo_stream_close(streamID1);
+    cdo_stream_close(streamID2);
+    cdo_stream_close(streamID1);
 
-  if (gridIDcurvl != -1) gridDestroy(gridIDcurvl);  // at the end must Free the allocated curvilinear grid definition...
+    if (gridIDcurvl != -1) gridDestroy(gridIDcurvl);  // at the end must Free the allocated curvilinear grid definition...
 
-  cdo_finish();
+    return 0;
+  }
 
-  return 0;
-}
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     // clang-format off
-    UVDESTAG     = cdo_operator_add("uvDestag",     0, 0, nullptr);
-    ROTUVNORTH   = cdo_operator_add("rotuvNorth",   0, 0, nullptr);
-    ROTUVN       = cdo_operator_add("rotuvN",       0, 0, nullptr);
+UVDESTAG = module.get_id("uvDestag");
+ROTUVNORTH = module.get_id("rotuvNorth");
+ROTUVN = module.get_id("rotuvN");
     // Cylindrical Equidistant projection
-    PROJUVLATLON = cdo_operator_add("projuvLatLon", 0, 0, nullptr);
+PROJUVLATLON = module.get_id("projuvLatLon");
     // clang-format on
 
     operatorID = cdo_operator_id();
@@ -1530,14 +1541,3 @@ public:
     // TODO: split the TransformUV and DestaggerUV into seperate classes and fix their close
   }
 };
-
-void *
-WindTrans(void *process)
-{
-  ModuleWindtrans windtrans;
-  windtrans.init(process);
-  windtrans.run();
-  windtrans.close();
-
-  return nullptr;
-}
diff --git a/src/Writegrid.cc b/src/Writegrid.cc
index cf3d073dc623e7a79a3c71093e994d2ad28c981c..0d83790d28a312ea67495eb23f5e8f0659adbe3a 100644
--- a/src/Writegrid.cc
+++ b/src/Writegrid.cc
@@ -17,18 +17,27 @@
 #include <mpim_grid.h>
 #include "griddes.h"
 
-class ModuleWriteGrid
+class Writegrid : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Writegrid",
+    .operators = { { "writegrid"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Writegrid> registration = RegisterEntry<Writegrid>(module);
   CdoStreamID streamID;
   int gridID;
   std::vector<int> mask;
 
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
 
     operator_check_argc(0);
 
@@ -43,7 +52,7 @@ public:
 
     if (!gridHasCoordinates(gridID)) cdo_abort("Cell corner coordinates missing!");
 
-    mask =std::vector<int>(gridsize);
+    mask = std::vector<int>(gridsize);
 
     if (gridInqMask(gridID, nullptr)) { gridInqMask(gridID, mask.data()); }
     else
@@ -62,16 +71,3 @@ public:
     cdo_stream_close(streamID);
   }
 };
-
-void *
-Writegrid(void *process)
-{
-
-  ModuleWriteGrid writegrid;
-  writegrid.init(process);
-  writegrid.run();
-  writegrid.close();
-  cdo_finish();
-
-  return nullptr;
-}
diff --git a/src/Writerandom.cc b/src/Writerandom.cc
index 54ab34effe558fec0474ced3438326cf9512b67b..69dd2e21930726662afafe64665741164ade4cde 100644
--- a/src/Writerandom.cc
+++ b/src/Writerandom.cc
@@ -16,8 +16,19 @@
 
 #include "process_int.h"
 
-class ModuleWriteRandom
+class Writerandom : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Writerandom",
+    .operators = { { "writerandom"} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Writerandom> registration = RegisterEntry<Writerandom>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -28,11 +39,9 @@ class ModuleWriteRandom
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -64,7 +73,7 @@ public:
 
         Varray2D<double> recdata(nrecs);
         std::vector<int> recvarID(nrecs), reclevelID(nrecs), recindex(nrecs);
-        std::vector<size_t> recnmiss(nrecs);
+        std::vector<size_t> recnumMissVals(nrecs);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
@@ -73,7 +82,7 @@ public:
             recvarID[recID] = varID;
             reclevelID[recID] = levelID;
             recdata[recID].resize(varList1[varID].gridsize);
-            cdo_read_record(streamID1, recdata[recID].data(), &recnmiss[recID]);
+            cdo_read_record(streamID1, recdata[recID].data(), &recnumMissVals[recID]);
           }
 
         for (int recID = 0; recID < nrecs; ++recID) recindex[recID] = -1;
@@ -105,7 +114,7 @@ public:
             const auto varID = recvarID[rindex];
             const auto levelID = reclevelID[rindex];
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, recdata[rindex].data(), recnmiss[rindex]);
+            cdo_write_record(streamID2, recdata[rindex].data(), recnumMissVals[rindex]);
           }
 
         tsID++;
@@ -116,18 +125,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Writerandom(void *process)
-{
-  ModuleWriteRandom writerandom;
-  writerandom.init(process);
-  writerandom.run();
-  writerandom.close();
-
-  return nullptr;
-}
diff --git a/src/XTimstat.cc b/src/XTimstat.cc
index 966d83f6116245d6c52f0181d42e0b601118966b..b8f7b5996914cc088b1c0d14f92c050ce2975e5e 100644
--- a/src/XTimstat.cc
+++ b/src/XTimstat.cc
@@ -69,7 +69,7 @@
 #include "param_conversion.h"
 #include "field_functions.h"
 
-#define USE_CDI_STREAM 1
+#define USE_CDI_STREAM 0
 
 struct ReadArguments
 {
@@ -113,20 +113,20 @@ cdoReadTimestep(void *rarg)
           recList[recID].levelID = levelID;
         }
 
-      size_t nmiss;
+      size_t numMissVals;
 
 #ifdef USE_CDI_STREAM
       if (Options::CDO_Memtype == MemType::Float)
-        streamReadRecordF(streamID, input_vars[varID][levelID].vec_f.data(), &nmiss);
+        streamReadRecordF(streamID, input_vars[varID][levelID].vec_f.data(), &numMissVals);
       else
-        streamReadRecord(streamID, input_vars[varID][levelID].vec_d.data(), &nmiss);
+        streamReadRecord(streamID, input_vars[varID][levelID].vec_d.data(), &numMissVals);
 #else
       if (Options::CDO_Memtype == MemType::Float)
-        cdo_read_record_f(streamID, input_vars[varID][levelID].vec_f.data(), &nmiss);
+        cdo_read_record_f(streamID, input_vars[varID][levelID].vec_f.data(), &numMissVals);
       else
-        cdo_read_record(streamID, input_vars[varID][levelID].vec_d.data(), &nmiss);
+        cdo_read_record(streamID, input_vars[varID][levelID].vec_d.data(), &numMissVals);
 #endif
-      input_vars[varID][levelID].nmiss = nmiss;
+      input_vars[varID][levelID].numMissVals = numMissVals;
     }
 
 #ifdef USE_CDI_STREAM
@@ -150,273 +150,317 @@ vlistSetFrequency(int vlistID, int compareDate)
   if (freq) cdiDefAttTxt(vlistID, CDI_GLOBAL, "frequency", (int) strlen(freq), freq);
 }
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("xtimmin",    FieldFunc_Min,   CMP_DATE, nullptr);
-  cdo_operator_add("xtimmax",    FieldFunc_Max,   CMP_DATE, nullptr);
-  cdo_operator_add("xtimsum",    FieldFunc_Sum,   CMP_DATE, nullptr);
-  cdo_operator_add("xtimmean",   FieldFunc_Mean,  CMP_DATE, nullptr);
-  cdo_operator_add("xtimavg",    FieldFunc_Avg,   CMP_DATE, nullptr);
-  cdo_operator_add("xtimvar",    FieldFunc_Var,   CMP_DATE, nullptr);
-  cdo_operator_add("xtimvar1",   FieldFunc_Var1,  CMP_DATE, nullptr);
-  cdo_operator_add("xtimstd",    FieldFunc_Std,   CMP_DATE, nullptr);
-  cdo_operator_add("xtimstd1",   FieldFunc_Std1,  CMP_DATE, nullptr);
-  cdo_operator_add("xyearmin",   FieldFunc_Min,   CMP_YEAR, nullptr);
-  cdo_operator_add("xyearmax",   FieldFunc_Max,   CMP_YEAR, nullptr);
-  cdo_operator_add("xyearsum",   FieldFunc_Sum,   CMP_YEAR, nullptr);
-  cdo_operator_add("xyearmean",  FieldFunc_Mean,  CMP_YEAR, nullptr);
-  cdo_operator_add("xyearavg",   FieldFunc_Avg,   CMP_YEAR, nullptr);
-  cdo_operator_add("xyearvar",   FieldFunc_Var,   CMP_YEAR, nullptr);
-  cdo_operator_add("xyearvar1",  FieldFunc_Var1,  CMP_YEAR, nullptr);
-  cdo_operator_add("xyearstd",   FieldFunc_Std,   CMP_YEAR, nullptr);
-  cdo_operator_add("xyearstd1",  FieldFunc_Std1,  CMP_YEAR, nullptr);
-  cdo_operator_add("xmonmin",    FieldFunc_Min,   CMP_MONTH, nullptr);
-  cdo_operator_add("xmonmax",    FieldFunc_Max,   CMP_MONTH, nullptr);
-  cdo_operator_add("xmonsum",    FieldFunc_Sum,   CMP_MONTH, nullptr);
-  cdo_operator_add("xmonmean",   FieldFunc_Mean,  CMP_MONTH, nullptr);
-  cdo_operator_add("xmonavg",    FieldFunc_Avg,   CMP_MONTH, nullptr);
-  cdo_operator_add("xmonvar",    FieldFunc_Var,   CMP_MONTH, nullptr);
-  cdo_operator_add("xmonvar1",   FieldFunc_Var1,  CMP_MONTH, nullptr);
-  cdo_operator_add("xmonstd",    FieldFunc_Std,   CMP_MONTH, nullptr);
-  cdo_operator_add("xmonstd1",   FieldFunc_Std1,  CMP_MONTH, nullptr);
-  // clang-format on
-}
-
-void *
-XTimstat(void *process)
+class XTimstat : public Process
 {
-  constexpr auto timestatDate{ TimeStat::MEAN };
+  TimeStat timestatDate{ TimeStat::MEAN };
   CdiDateTime vDateTime0{};
   CdiDateTime vDateTimeN{};
   CdoStreamID streamID3 = CDO_STREAM_UNDEF;
-  int vlistID3, taxisID3 = -1;
   bool lvfrac = false;
   double vfrac = 1;
 
-  cdo_initialize(process);
+  int tsID = 0;
+  int otsID = 0;
+  int nrecs;
+
+  int compareDate;
 
-  addOperators();
+#ifdef USE_CDI_STREAM
+  int streamID1;
+#else
+  CdoStreamID streamID1;
+#endif
+  CdoStreamID streamID2;
 
-  auto operatorID = cdo_operator_id();
-  auto operfunc = cdo_operator_f1(operatorID);
-  auto compareDate = cdo_operator_f2(operatorID);
+  int vlistID1;
+  int vlistID3;
 
-  auto lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-  auto lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-  auto lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-  auto lvars2 = lvarstd;
-  int divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+  int taxisID1;
+  int taxisID2;
+  int taxisID3 = -1;
 
-  auto field2_stdvar_func = lstd ? field2_std : field2_var;
-  auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+  DateTimeList dtlist;
+  std::vector<RecordInfo> recList;
+  FieldVector3D input_vars = FieldVector3D(2);
 
-  if (operfunc == FieldFunc_Mean)
-    {
-      auto oargc = cdo_operator_argc();
-      if (oargc == 1)
-        {
-          lvfrac = true;
-          vfrac = parameter_to_double(cdo_operator_argv(0));
-          if (Options::cdoVerbose) cdo_print("Set vfrac to %g", vfrac);
-          if (vfrac < 0 || vfrac > 1) cdo_abort("vfrac out of range!");
-        }
-      else if (oargc > 1)
-        cdo_abort("Too many arguments!");
-    }
-  else { operator_check_argc(0); }
+  int curFirst = 0;
+  //int curSecond = 1;
+
+  bool lparallelread = false;
+  bool ltsfirst = true;
+  cdo::Task *read_task = nullptr;
+  void *readresult = nullptr;
+
+  int maxrecs = 0;
+  FieldVector2D samp1, vars1, vars2;
+
+  int operatorID;
+  int operfunc;
+
+  int lmean, lstd, lvarstd, lvars2, divisor;
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "XTimstat",
+    .operators = { { "xtimmin", FieldFunc_Min, CMP_DATE, nullptr},    { "xtimmax", FieldFunc_Max, CMP_DATE, nullptr},
+                   { "xtimsum", FieldFunc_Sum, CMP_DATE, nullptr},    { "xtimmean", FieldFunc_Mean, CMP_DATE, nullptr},
+                   { "xtimavg", FieldFunc_Avg, CMP_DATE, nullptr},    { "xtimvar", FieldFunc_Var, CMP_DATE, nullptr},
+                   { "xtimvar1", FieldFunc_Var1, CMP_DATE, nullptr},  { "xtimstd", FieldFunc_Std, CMP_DATE, nullptr},
+                   { "xtimstd1", FieldFunc_Std1, CMP_DATE, nullptr},  { "xyearmin", FieldFunc_Min, CMP_YEAR, nullptr},
+                   { "xyearmax", FieldFunc_Max, CMP_YEAR, nullptr},   { "xyearsum", FieldFunc_Sum, CMP_YEAR, nullptr},
+                   { "xyearmean", FieldFunc_Mean, CMP_YEAR, nullptr}, { "xyearavg", FieldFunc_Avg, CMP_YEAR, nullptr},
+                   { "xyearvar", FieldFunc_Var, CMP_YEAR, nullptr},   { "xyearvar1", FieldFunc_Var1, CMP_YEAR, nullptr},
+                   { "xyearstd", FieldFunc_Std, CMP_YEAR, nullptr},   { "xyearstd1", FieldFunc_Std1, CMP_YEAR, nullptr},
+                   { "xmonmin", FieldFunc_Min, CMP_MONTH, nullptr},   { "xmonmax", FieldFunc_Max, CMP_MONTH, nullptr},
+                   { "xmonsum", FieldFunc_Sum, CMP_MONTH, nullptr},   { "xmonmean", FieldFunc_Mean, CMP_MONTH, nullptr},
+                   { "xmonavg", FieldFunc_Avg, CMP_MONTH, nullptr},   { "xmonvar", FieldFunc_Var, CMP_MONTH, nullptr},
+                   { "xmonvar1", FieldFunc_Var1, CMP_MONTH, nullptr}, { "xmonstd", FieldFunc_Std, CMP_MONTH, nullptr},
+                   { "xmonstd1", FieldFunc_Std1, CMP_MONTH, nullptr} },
+    .aliases = {},
+    .mode = INTERNAL,    // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<XTimstat> registration = RegisterEntry<XTimstat>(module);
+
+  void
+  init()
+  {
+    operatorID = cdo_operator_id();
+    operfunc = cdo_operator_f1(operatorID);
+    compareDate = cdo_operator_f2(operatorID);
+
+    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
+    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
+    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
+    lvars2 = lvarstd;
+    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+
+    if (operfunc == FieldFunc_Mean)
+      {
+        auto oargc = cdo_operator_argc();
+        if (oargc == 1)
+          {
+            lvfrac = true;
+            vfrac = parameter_to_double(cdo_operator_argv(0));
+            if (Options::cdoVerbose) cdo_print("Set vfrac to %g", vfrac);
+            if (vfrac < 0 || vfrac > 1) cdo_abort("vfrac out of range!");
+          }
+        else if (oargc > 1)
+          cdo_abort("Too many arguments!");
+      }
+    else { operator_check_argc(0); }
 
 #ifdef USE_CDI_STREAM
-  auto streamID1 = streamOpenRead(cdo_get_stream_name(0));
-  auto vlistID1 = streamInqVlist(streamID1);
+    streamID1 = streamOpenRead(cdo_get_stream_name(0));
+    vlistID1 = streamInqVlist(streamID1);
 #else
-  auto streamID1 = cdo_open_read(0);
-  auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    streamID1 = cdo_open_read(0);
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
 #endif
 
-  auto vlistID2 = vlistDuplicate(vlistID1);
-  vlist_define_timestep_type(vlistID2, operfunc);
+    auto vlistID2 = vlistDuplicate(vlistID1);
+    vlist_define_timestep_type(vlistID2, operfunc);
 
-  vlistDefNtsteps(vlistID2, (compareDate == CMP_DATE) ? 1 : -1);
+    vlistDefNtsteps(vlistID2, (compareDate == CMP_DATE) ? 1 : -1);
 
-  auto taxisID1 = vlistInqTaxis(vlistID1);
-  auto taxisID2 = taxisDuplicate(taxisID1);
-  taxisWithBounds(taxisID2);
-  if (taxisInqType(taxisID2) == TAXIS_FORECAST) taxisDefType(taxisID2, TAXIS_RELATIVE);
-  vlistDefTaxis(vlistID2, taxisID2);
+    taxisID1 = vlistInqTaxis(vlistID1);
+    taxisID2 = taxisDuplicate(taxisID1);
+    taxisWithBounds(taxisID2);
+    if (taxisInqType(taxisID2) == TAXIS_FORECAST) taxisDefType(taxisID2, TAXIS_RELATIVE);
+    vlistDefTaxis(vlistID2, taxisID2);
 
-  auto nvars = vlistNvars(vlistID1);
+    auto nvars = vlistNvars(vlistID1);
 
-  vlistSetFrequency(vlistID2, compareDate);
+    vlistSetFrequency(vlistID2, compareDate);
 
-  auto streamID2 = cdo_open_write(1);
-  cdo_def_vlist(streamID2, vlistID2);
+    streamID2 = cdo_open_write(1);
+    cdo_def_vlist(streamID2, vlistID2);
 
-  if (Options::cdoDiag)
-    {
-      char filename[8192];
-      std::strcpy(filename, cdo_operator_name(operatorID));
-      strcat(filename, "_");
-      strcat(filename, cdo_get_stream_name(1));
-      streamID3 = cdo_open_write(filename);
+    if (Options::cdoDiag)
+      {
+        char filename[8192];
+        std::strcpy(filename, cdo_operator_name(operatorID));
+        strcat(filename, "_");
+        strcat(filename, cdo_get_stream_name(1));
+        streamID3 = cdo_open_write(filename);
 
-      vlistID3 = vlistDuplicate(vlistID1);
+        vlistID3 = vlistDuplicate(vlistID1);
 
-      for (int varID = 0; varID < nvars; ++varID)
-        {
-          vlistDefVarDatatype(vlistID3, varID, CDI_DATATYPE_INT32);
-          vlistDefVarMissval(vlistID3, varID, -1);
-          cdiDefKeyString(vlistID3, varID, CDI_KEY_UNITS, "");
-          cdiDeleteKey(vlistID3, varID, CDI_KEY_ADDOFFSET);
-          cdiDeleteKey(vlistID3, varID, CDI_KEY_SCALEFACTOR);
-        }
+        for (int varID = 0; varID < nvars; ++varID)
+          {
+            vlistDefVarDatatype(vlistID3, varID, CDI_DATATYPE_INT32);
+            vlistDefVarMissval(vlistID3, varID, -1);
+            cdiDefKeyString(vlistID3, varID, CDI_KEY_UNITS, "");
+            cdiDeleteKey(vlistID3, varID, CDI_KEY_ADDOFFSET);
+            cdiDeleteKey(vlistID3, varID, CDI_KEY_SCALEFACTOR);
+          }
 
-      taxisID3 = taxisDuplicate(taxisID1);
-      taxisWithBounds(taxisID3);
-      vlistDefTaxis(vlistID3, taxisID3);
+        taxisID3 = taxisDuplicate(taxisID1);
+        taxisWithBounds(taxisID3);
+        vlistDefTaxis(vlistID3, taxisID3);
 
-      cdo_def_vlist(streamID3, vlistID3);
-    }
+        cdo_def_vlist(streamID3, vlistID3);
+      }
 
-  DateTimeList dtlist;
-  dtlist.set_stat(timestatDate);
-  dtlist.set_calendar(taxisInqCalendar(taxisID1));
-
-  int FIELD_MEMTYPE = 0;
-  if (operfunc == FieldFunc_Mean && Options::CDO_Memtype == MemType::Float) FIELD_MEMTYPE = FIELD_FLT;
-  FieldVector3D input_vars(2);
-  fields_from_vlist(vlistID1, input_vars[0], FIELD_VEC | FIELD_MEMTYPE);
-  fields_from_vlist(vlistID1, input_vars[1], FIELD_VEC | FIELD_MEMTYPE);
-  FieldVector2D samp1, vars1, vars2;
-  fields_from_vlist(vlistID1, samp1);
-  fields_from_vlist(vlistID1, vars1, FIELD_VEC);
-  if (lvars2) fields_from_vlist(vlistID1, vars2, FIELD_VEC);
+    dtlist.set_stat(timestatDate);
+    dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-  int curFirst = 0;
-  int curSecond = 1;
-  (void) curSecond;
+    int FIELD_MEMTYPE = 0;
+    if (operfunc == FieldFunc_Mean && Options::CDO_Memtype == MemType::Float) FIELD_MEMTYPE = FIELD_FLT;
+    fields_from_vlist(vlistID1, input_vars[0], FIELD_VEC | FIELD_MEMTYPE);
+    fields_from_vlist(vlistID1, input_vars[1], FIELD_VEC | FIELD_MEMTYPE);
 
-  ReadArguments readarg(input_vars[curFirst]);
-  readarg.streamID = streamID1;
+    fields_from_vlist(vlistID1, samp1);
+    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
+    if (lvars2) fields_from_vlist(vlistID1, vars2, FIELD_VEC);
 
-  bool lparallelread = Options::CDO_Parallel_Read > 0;
-  bool ltsfirst = true;
-  cdo::Task *read_task = nullptr;
-  void *readresult = nullptr;
+    lparallelread = (Options::CDO_Parallel_Read > 0);
+    if (lparallelread) read_task = new cdo::Task;
 
-  if (lparallelread) read_task = new cdo::Task;
-
-  int tsID = 0;
-  int otsID = 0;
 #ifdef USE_CDI_STREAM
-  auto nrecs = streamInqTimestep(streamID1, tsID);
+    nrecs = streamInqTimestep(streamID1, tsID);
 #else
-  auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+    nrecs = cdo_stream_inq_timestep(streamID1, tsID);
 #endif
-  int maxrecs = nrecs;
-  std::vector<RecordInfo> recList(maxrecs);
 
-  tsID++;
-  while (true)
-    {
-      int numSets = 0;
-      while (nrecs > 0)
-        {
-          dtlist.taxis_inq_timestep(taxisID1, numSets);
-          auto vDateTime = dtlist.get_vDateTime(numSets);
+    maxrecs = nrecs;
+    recList = std::vector<RecordInfo>(maxrecs);
+  }
 
-          if (numSets == 0) vDateTime0 = vDateTime;
+  void
+  run()
+  {
+    auto field2_stdvar_func = lstd ? field2_std : field2_var;
+    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
 
-          if (date_is_neq(vDateTime, vDateTime0, compareDate))
-            {
-              cdo_add_steps(-1);
-              break;
-            }
+    tsID++;
 
-          readarg.tsIDnext = tsID;
-          readarg.nrecs = nrecs;
-          readarg.recList = recList.data();
-          readarg.vars = input_vars[curFirst];
+    ReadArguments readarg(input_vars[curFirst]);
+    readarg.streamID = streamID1;
 
-          if (ltsfirst || !lparallelread)
-            {
-              ltsfirst = false;
-              readresult = cdoReadTimestep(&readarg);
-            }
-          else { readresult = read_task->wait(); }
+    while (true)
+      {
+        int numSets = 0;
+        while (nrecs > 0)
+          {
+            dtlist.taxis_inq_timestep(taxisID1, numSets);
+            auto vDateTime = dtlist.get_vDateTime(numSets);
 
-          nrecs = *(int *) readresult;
+            if (numSets == 0) vDateTime0 = vDateTime;
 
-          // std::swap(curFirst, curSecond);
+            if (date_is_neq(vDateTime, vDateTime0, compareDate))
+              {
+                cdo_add_steps(-1);
+                break;
+              }
 
-          if (nrecs && lparallelread)
-            {
-              readarg.vars = input_vars[curFirst];
-              readarg.tsIDnext = tsID + 1;
-              read_task->start(cdoReadTimestep, &readarg);
-            }
+            readarg.tsIDnext = tsID;
+            readarg.nrecs = nrecs;
+            readarg.recList = recList.data();
+            readarg.vars = input_vars[curFirst];
 
-          if (numSets == 0)
-            {
+            if (ltsfirst || !lparallelread)
+              {
+                ltsfirst = false;
+                readresult = cdoReadTimestep(&readarg);
+              }
+            else { readresult = read_task->wait(); }
+
+            nrecs = *(int *) readresult;
+
+            // std::swap(curFirst, curSecond);
+
+            if (nrecs && lparallelread)
+              {
+                readarg.vars = input_vars[curFirst];
+                readarg.tsIDnext = tsID + 1;
+                read_task->start(cdoReadTimestep, &readarg);
+              }
+
+            if (numSets == 0)
+              {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) if (maxrecs > 1)
 #endif
-              for (int recID = 0; recID < maxrecs; ++recID)
-                {
-                  auto varID = recList[recID].varID;
-                  auto levelID = recList[recID].levelID;
+                for (int recID = 0; recID < maxrecs; ++recID)
+                  {
+                    auto varID = recList[recID].varID;
+                    auto levelID = recList[recID].levelID;
 
-                  auto &rsamp1 = samp1[varID][levelID];
-                  auto &rvars1 = vars1[varID][levelID];
-                  auto &rinput_var = input_vars[curFirst][varID][levelID];
+                    auto &rsamp1 = samp1[varID][levelID];
+                    auto &rvars1 = vars1[varID][levelID];
+                    auto &rinput_var = input_vars[curFirst][varID][levelID];
 
-                  auto nmiss = rinput_var.nmiss;
+                    auto numMissVals = rinput_var.numMissVals;
 
-                  field_copy(rinput_var, rvars1);
-                  rvars1.nmiss = nmiss;
-                  if (nmiss || !rsamp1.empty())
-                    {
-                      auto fieldsize = rvars1.size;
-                      if (rsamp1.empty()) rsamp1.resize(fieldsize);
+                    field_copy(rinput_var, rvars1);
+                    rvars1.numMissVals = numMissVals;
+                    if (numMissVals || !rsamp1.empty())
+                      {
+                        auto fieldsize = rvars1.size;
+                        if (rsamp1.empty()) rsamp1.resize(fieldsize);
 
-                      for (size_t i = 0; i < fieldsize; ++i) rsamp1.vec_d[i] = !DBL_IS_EQUAL(rvars1.vec_d[i], rvars1.missval);
-                    }
-                }
-            }
-          else
-            {
+                        for (size_t i = 0; i < fieldsize; ++i) rsamp1.vec_d[i] = !DBL_IS_EQUAL(rvars1.vec_d[i], rvars1.missval);
+                      }
+                  }
+              }
+            else
+              {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) if (maxrecs > 1)
 #endif
+                for (int recID = 0; recID < maxrecs; ++recID)
+                  {
+                    auto varID = recList[recID].varID;
+                    auto levelID = recList[recID].levelID;
+
+                    auto &rsamp1 = samp1[varID][levelID];
+                    auto &rvars1 = vars1[varID][levelID];
+                    auto &rinput_var = input_vars[curFirst][varID][levelID];
+
+                    auto numMissVals = rinput_var.numMissVals;
+
+                    if (numMissVals || !rsamp1.empty())
+                      {
+                        auto fieldsize = rvars1.size;
+                        if (rsamp1.empty()) rsamp1.resize(fieldsize, numSets);
+
+                        for (size_t i = 0; i < fieldsize; ++i)
+                          if (!DBL_IS_EQUAL(rinput_var.vec_d[i], rvars1.missval)) rsamp1.vec_d[i]++;
+                      }
+
+                    // clang-format off
+                    if (lvarstd) field2_sumsumq(rvars1, vars2[varID][levelID], rinput_var);
+                    else         field2_function(rvars1, rinput_var, operfunc);
+                    // clang-format on
+                  }
+              }
+
+            if (numSets == 0 && lvarstd)
               for (int recID = 0; recID < maxrecs; ++recID)
                 {
                   auto varID = recList[recID].varID;
                   auto levelID = recList[recID].levelID;
 
-                  auto &rsamp1 = samp1[varID][levelID];
-                  auto &rvars1 = vars1[varID][levelID];
-                  auto &rinput_var = input_vars[curFirst][varID][levelID];
-
-                  auto nmiss = rinput_var.nmiss;
+                  if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
 
-                  if (nmiss || !rsamp1.empty())
-                    {
-                      auto fieldsize = rvars1.size;
-                      if (rsamp1.empty()) rsamp1.resize(fieldsize, numSets);
+                  field2_moq(vars2[varID][levelID], vars1[varID][levelID]);
+                }
 
-                      for (size_t i = 0; i < fieldsize; ++i)
-                        if (!DBL_IS_EQUAL(rinput_var.vec_d[i], rvars1.missval)) rsamp1.vec_d[i]++;
-                    }
+            vDateTimeN = vDateTime;
+            numSets++;
+            tsID++;
+          }
 
-                  // clang-format off
-                  if (lvarstd) field2_sumsumq(rvars1, vars2[varID][levelID], rinput_var);
-                  else         field2_function(rvars1, rinput_var, operfunc);
-                  // clang-format on
-                }
-            }
+        if (nrecs == 0 && numSets == 0) break;
 
-          if (numSets == 0 && lvarstd)
+        if (lmean)
+          {
+#ifdef _OPENMP
+#pragma omp parallel for default(shared) if (maxrecs > 1)
+#endif
             for (int recID = 0; recID < maxrecs; ++recID)
               {
                 auto varID = recList[recID].varID;
@@ -424,128 +468,108 @@ XTimstat(void *process)
 
                 if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
 
-                field2_moq(vars2[varID][levelID], vars1[varID][levelID]);
+                if (!samp1[varID][levelID].empty())
+                  field2_div(vars1[varID][levelID], samp1[varID][levelID]);
+                else
+                  fieldc_div(vars1[varID][levelID], (double) numSets);
               }
+          }
+        else if (lvarstd)
+          {
+            for (int recID = 0; recID < maxrecs; ++recID)
+              {
+                auto varID = recList[recID].varID;
+                auto levelID = recList[recID].levelID;
 
-          vDateTimeN = vDateTime;
-          numSets++;
-          tsID++;
-        }
+                if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
 
-      if (nrecs == 0 && numSets == 0) break;
+                const auto &rsamp1 = samp1[varID][levelID];
+                auto &rvars1 = vars1[varID][levelID];
+                const auto &rvars2 = vars2[varID][levelID];
 
-      if (lmean)
-        {
-#ifdef _OPENMP
-#pragma omp parallel for default(shared) if (maxrecs > 1)
-#endif
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto varID = recList[recID].varID;
-              auto levelID = recList[recID].levelID;
+                if (!rsamp1.empty())
+                  field2_stdvar_func(rvars1, rvars2, rsamp1, divisor);
+                else
+                  fieldc_stdvar_func(rvars1, rvars2, numSets, divisor);
+              }
+          }
 
-              if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
+        if (Options::cdoVerbose) cdo_print("%s  vfrac = %g, numSets = %d", datetime_to_string(vDateTimeN), vfrac, numSets);
 
-              if (!samp1[varID][levelID].empty())
-                field2_div(vars1[varID][levelID], samp1[varID][levelID]);
-              else
-                fieldc_div(vars1[varID][levelID], (double) numSets);
-            }
-        }
-      else if (lvarstd)
-        {
+        if (lvfrac && operfunc == FieldFunc_Mean)
           for (int recID = 0; recID < maxrecs; ++recID)
             {
               auto varID = recList[recID].varID;
               auto levelID = recList[recID].levelID;
+              auto &rvars1 = vars1[varID][levelID];
 
               if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
 
-              const auto &rsamp1 = samp1[varID][levelID];
-              auto &rvars1 = vars1[varID][levelID];
-              const auto &rvars2 = vars2[varID][levelID];
+              auto missval = rvars1.missval;
+              if (!samp1[varID][levelID].empty())
+                {
+                  auto fieldsize = rvars1.size;
+                  size_t irun = 0;
+                  for (size_t i = 0; i < fieldsize; ++i)
+                    {
+                      if ((samp1[varID][levelID].vec_d[i] / numSets) < vfrac)
+                        {
+                          rvars1.vec_d[i] = missval;
+                          irun++;
+                        }
+                    }
 
-              if (!rsamp1.empty())
-                field2_stdvar_func(rvars1, rvars2, rsamp1, divisor);
-              else
-                fieldc_stdvar_func(rvars1, rvars2, numSets, divisor);
+                  if (irun) rvars1.numMissVals = field_num_miss(rvars1);
+                }
             }
-        }
 
-      if (Options::cdoVerbose) cdo_print("%s  vfrac = %g, numSets = %d", datetime_to_string(vDateTimeN), vfrac, numSets);
+        dtlist.stat_taxis_def_timestep(taxisID2, numSets);
+        cdo_def_timestep(streamID2, otsID);
+
+        if (Options::cdoDiag)
+          {
+            dtlist.stat_taxis_def_timestep(taxisID3, numSets);
+            cdo_def_timestep(streamID3, otsID);
+          }
 
-      if (lvfrac && operfunc == FieldFunc_Mean)
         for (int recID = 0; recID < maxrecs; ++recID)
           {
             auto varID = recList[recID].varID;
             auto levelID = recList[recID].levelID;
             auto &rvars1 = vars1[varID][levelID];
 
-            if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
+            if (otsID && vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
+
+            cdo_def_record(streamID2, varID, levelID);
+            cdo_write_record(streamID2, rvars1.vec_d.data(), rvars1.numMissVals);
 
-            auto missval = rvars1.missval;
-            if (!samp1[varID][levelID].empty())
+            if (Options::cdoDiag)
               {
-                auto fieldsize = rvars1.size;
-                size_t irun = 0;
-                for (size_t i = 0; i < fieldsize; ++i)
+                if (!samp1[varID][levelID].empty())
                   {
-                    if ((samp1[varID][levelID].vec_d[i] / numSets) < vfrac)
-                      {
-                        rvars1.vec_d[i] = missval;
-                        irun++;
-                      }
+                    cdo_def_record(streamID3, varID, levelID);
+                    cdo_write_record(streamID3, samp1[varID][levelID].vec_d.data(), 0);
                   }
-
-                if (irun) rvars1.nmiss = field_num_miss(rvars1);
               }
           }
 
-      dtlist.stat_taxis_def_timestep(taxisID2, numSets);
-      cdo_def_timestep(streamID2, otsID);
+        if (nrecs == 0) break;
+        otsID++;
+      }
+  }
 
-      if (Options::cdoDiag)
-        {
-          dtlist.stat_taxis_def_timestep(taxisID3, numSets);
-          cdo_def_timestep(streamID3, otsID);
-        }
-
-      for (int recID = 0; recID < maxrecs; ++recID)
-        {
-          auto varID = recList[recID].varID;
-          auto levelID = recList[recID].levelID;
-          auto &rvars1 = vars1[varID][levelID];
-
-          if (otsID && vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
+  void
+  close()
+  {
 
-          cdo_def_record(streamID2, varID, levelID);
-          cdo_write_record(streamID2, rvars1.vec_d.data(), rvars1.nmiss);
-
-          if (Options::cdoDiag)
-            {
-              if (!samp1[varID][levelID].empty())
-                {
-                  cdo_def_record(streamID3, varID, levelID);
-                  cdo_write_record(streamID3, samp1[varID][levelID].vec_d.data(), 0);
-                }
-            }
-        }
-
-      if (nrecs == 0) break;
-      otsID++;
-    }
-
-  if (Options::cdoDiag) cdo_stream_close(streamID3);
-  cdo_stream_close(streamID2);
+    if (Options::cdoDiag) cdo_stream_close(streamID3);
+    cdo_stream_close(streamID2);
 #ifdef USE_CDI_STREAM
-  streamClose(streamID1);
+    streamClose(streamID1);
 #else
-  cdo_stream_close(streamID1);
+    cdo_stream_close(streamID1);
 #endif
 
-  if (read_task) delete read_task;
-
-  cdo_finish();
-
-  return nullptr;
-}
+    if (read_task) delete read_task;
+  }
+};
diff --git a/src/Ydayarith.cc b/src/Ydayarith.cc
index 1265428620ca40f1f564f968271061d8419475f7..7ea26ccc18b087b25fa252a7cb5e87b6d841e4e1 100644
--- a/src/Ydayarith.cc
+++ b/src/Ydayarith.cc
@@ -22,17 +22,23 @@
 #include "printinfo.h"
 #include "field_functions.h"
 
-static void
-add_operators(void)
+class Ydayarith : public Process
 {
-  cdo_operator_add("ydayadd", FieldFunc_Add, 0, nullptr);
-  cdo_operator_add("ydaysub", FieldFunc_Sub, 0, nullptr);
-  cdo_operator_add("ydaymul", FieldFunc_Mul, 0, nullptr);
-  cdo_operator_add("ydaydiv", FieldFunc_Div, 0, nullptr);
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ydayarith",
+    .operators = { { "ydayadd", FieldFunc_Add, 0, YdayarithHelp },
+                   { "ydaysub", FieldFunc_Sub, 0, YdayarithHelp },
+                   { "ydaymul", FieldFunc_Mul, 0, YdayarithHelp },
+                   { "ydaydiv", FieldFunc_Div, 0, YdayarithHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ydayarith> registration = RegisterEntry<Ydayarith>(module);
 
-class ModuleYdayarith
-{
 private:
   static const int MaxDays = 373;  //~31*12
   Field field;
@@ -54,11 +60,8 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -87,7 +90,7 @@ public:
     cdo_def_vlist(streamID3, vlistID3);
   }
 
-  int
+  void
   run()
   {
     int tsID = 0;
@@ -102,13 +105,13 @@ public:
         if (dayOfYear == 0)
           {
             cdo_error("Day of year %d out of range (date=%s)!", dayOfYear, date_to_string(vDateTime.date));
-            return -1;
+            return;
           }
         if (vars2[dayOfYear].size() > 0)
           {
             cdo_error("Day of year index %d already allocated (date=%s)! Each day of year must only exist once", dayOfYear,
                       date_to_string(vDateTime.date));
-            return -1;
+            return;
           }
 
         fields_from_vlist(vlistID2, vars2[dayOfYear], FIELD_VEC | FIELD_NAT);
@@ -135,12 +138,12 @@ public:
         if (dayOfYear == 0)
           {
             cdo_error("Day of year %d out of range (date=%s)!", dayOfYear, date_to_string(vDateTime.date));
-            return -1;
+            return;
           }
         if (vars2[dayOfYear].size() == 0)
           {
             cdo_error("Day of year index %d not found (date=%s)!", dayOfYear, date_to_string(vDateTime.date));
-            return -1;
+            return;
           }
 
         cdo_taxis_copy_timestep(taxisID3, taxisID1);
@@ -160,7 +163,7 @@ public:
           }
         tsID++;
       }
-    return 0;
+    return;
   }
 
   void
@@ -169,19 +172,5 @@ public:
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Ydayarith(void *process)
-{
-  ModuleYdayarith ydayarith;
-  ydayarith.init(process);
-  auto success = ydayarith.run();
-  (void) success;
-  ydayarith.close();
-
-  return nullptr;
-}
diff --git a/src/Ydaypctl.cc b/src/Ydaypctl.cc
index 5d4710eeddb789b83e954e3a8e649ebe8c7472f2..0c7726d47e44d39c93097432185dca29fa0523e9 100644
--- a/src/Ydaypctl.cc
+++ b/src/Ydaypctl.cc
@@ -24,8 +24,19 @@
 #include "percentiles_hist.h"
 #include "field_functions.h"
 
-class ModuleYdaypctl
+class Ydaypctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ydaypctl",
+    .operators = { { "ydaypctl", FieldFunc_Pctl, 0, YdaypctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ydaypctl> registration = RegisterEntry<Ydaypctl>(module);
   static const int MaxDays = 373;
   CdiDateTime vDateTimes1[MaxDays]{};
   CdiDateTime vDateTimes2[MaxDays]{};
@@ -59,10 +70,8 @@ class ModuleYdaypctl
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("ydaypctl", FieldFunc_Pctl, 0, nullptr);
 
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
@@ -245,17 +254,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Ydaypctl(void *process)
-{
-  ModuleYdaypctl ydaypctl;
-  ydaypctl.init(process);
-  ydaypctl.run();
-  ydaypctl.close();
-
-  return nullptr;
-}
diff --git a/src/Ydaystat.cc b/src/Ydaystat.cc
index 27ebfb2ef653274b71708f814667bd74a4a560c9..913534130dd43a67d974e493d5ec8e9c7597d2d0 100644
--- a/src/Ydaystat.cc
+++ b/src/Ydaystat.cc
@@ -43,7 +43,7 @@ setParameter(void)
 
       KVList kvlist;
       kvlist.name = cdo_module_name();
-      if (kvlist.parse_arguments(pargc, pargv) != 0) cdo_abort("Parse error!");
+      if (kvlist.parse_arguments(pargv) != 0) cdo_abort("Parse error!");
       if (Options::cdoVerbose) kvlist.print();
 
       for (const auto &kv : kvlist)
@@ -61,24 +61,29 @@ setParameter(void)
     }
 }
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("ydayrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("ydaymin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("ydaymax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("ydaysum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("ydaymean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("ydayavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("ydayvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("ydayvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("ydaystd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("ydaystd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-class ModuleYdaystat
+class Ydaystat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ydaystat",
+    .operators = { { "ydayrange", FieldFunc_Range, 0, YdaystatHelp },
+                   { "ydaymin", FieldFunc_Min, 0, YdaystatHelp },
+                   { "ydaymax", FieldFunc_Max, 0, YdaystatHelp },
+                   { "ydaysum", FieldFunc_Sum, 0, YdaystatHelp },
+                   { "ydaymean", FieldFunc_Mean, 0, YdaystatHelp },
+                   { "ydayavg", FieldFunc_Avg, 0, YdaystatHelp },
+                   { "ydaystd", FieldFunc_Std, 0, YdaystatHelp },
+                   { "ydaystd1", FieldFunc_Std1, 0, YdaystatHelp },
+                   { "ydayvar", FieldFunc_Var, 0, YdaystatHelp },
+                   { "ydayvar1", FieldFunc_Var1, 0, YdaystatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ydaystat> registration = RegisterEntry<Ydaystat>(module);
+
 private:
   static const int MaxDays = 373;
 
@@ -111,14 +116,10 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
     setParameter();
 
-    addOperators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -201,11 +202,11 @@ public:
                 cdo_read_record(streamID1, rvars1);
                 if (lrange)
                   {
-                    vars2[dayOfYear][varID][levelID].nmiss = rvars1.nmiss;
+                    vars2[dayOfYear][varID][levelID].numMissVals = rvars1.numMissVals;
                     vars2[dayOfYear][varID][levelID].vec_d = rvars1.vec_d;
                   }
 
-                if (rvars1.nmiss || !rsamp1.empty())
+                if (rvars1.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                     field2_vinit(rsamp1, rvars1);
@@ -216,7 +217,7 @@ public:
                 field.init(var);
                 cdo_read_record(streamID1, field);
 
-                if (field.nmiss || !rsamp1.empty())
+                if (field.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                     field2_vincr(rsamp1, field);
@@ -313,18 +314,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Ydaystat(void *process)
-{
-  ModuleYdaystat ydaystat;
-  ydaystat.init(process);
-  ydaystat.run();
-  ydaystat.close();
-
-  return nullptr;
-}
diff --git a/src/Ydrunpctl.cc b/src/Ydrunpctl.cc
index 1f6e0f48934279b9b32ec3f5383ba749dc147957..036aa3f5a5939a4ef9f93c5c0c1d7fb97bb9d40c 100644
--- a/src/Ydrunpctl.cc
+++ b/src/Ydrunpctl.cc
@@ -28,8 +28,20 @@
 #include "field_functions.h"
 
 constexpr int MaxDays = 373;
-class ModuleYdrunpctl
+class Ydrunpctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ydrunpctl",
+    .operators = { { "ydrunpctl", FieldFunc_Pctl, 0, YdrunpctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ydrunpctl> registration = RegisterEntry<Ydrunpctl>(module);
+
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -67,13 +79,10 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
     vars2 = std::vector<bool>(MaxDays, false);
 
-    cdo_initialize(process);
-    cdo_operator_add("ydrunpctl", FieldFunc_Pctl, 0, nullptr);
-
     pn = parameter_to_double(cdo_operator_argv(0));
     ndates = parameter_to_int(cdo_operator_argv(1));
 
@@ -82,7 +91,7 @@ public:
         auto params = cdo_get_oper_argv();
         params = std::vector<std::string>(params.begin() + 2, params.end());
         KVList kvlist;
-        if (kvlist.parse_arguments(cdo_operator_argc() - 2, params) != 0) cdo_abort("Argument parse error!");
+        if (kvlist.parse_arguments(params) != 0) cdo_abort("Argument parse error!");
         auto kv = kvlist.search("pm");
         if (kv && kv->nvalues > 0) percMethod = parameter_to_word(kv->values[0]);
         kv = kvlist.search("rm");
@@ -286,9 +295,9 @@ public:
                 streamInqRecord(cdiStream, &varID, &levelID);
                 auto &pvars1 = vars1[ndates - 1][varID][levelID];
                 if (pvars1.memType == MemType::Float)
-                  streamReadRecordF(cdiStream, pvars1.vec_f.data(), &pvars1.nmiss);
+                  streamReadRecordF(cdiStream, pvars1.vec_f.data(), &pvars1.numMissVals);
                 else
-                  streamReadRecord(cdiStream, pvars1.vec_d.data(), &pvars1.nmiss);
+                  streamReadRecord(cdiStream, pvars1.vec_d.data(), &pvars1.numMissVals);
               }
 
             cdiDateTimes[ndates] = datetime_avg(dpy, ndates, cdiDateTimes);
@@ -383,18 +392,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Ydrunpctl(void *process)
-{
-  ModuleYdrunpctl ydrunstat;
-  ydrunstat.init(process);
-  ydrunstat.run();
-  ydrunstat.close();
-
-  return nullptr;
-}
diff --git a/src/Ydrunstat.cc b/src/Ydrunstat.cc
index 7b355913a0d9985cd4f6d19de32112df778d4062..8f4fabc0c0c24c250598452dadcd64b47ea165a5 100644
--- a/src/Ydrunstat.cc
+++ b/src/Ydrunstat.cc
@@ -123,24 +123,28 @@ ydstat_finalize(YdayStats &stats, int operfunc)
       }
 }
 
-static void
-addOperators(void)
+class Ydrunstat : public Process
 {
-  // clang-format off
-  cdo_operator_add("ydrunmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("ydrunmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("ydrunsum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("ydrunmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("ydrunavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("ydrunvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("ydrunvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("ydrunstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("ydrunstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ydrunstat",
+    .operators = { { "ydrunmin", FieldFunc_Min, 0, YdrunstatHelp },
+                   { "ydrunmax", FieldFunc_Max, 0, YdrunstatHelp },
+                   { "ydrunsum", FieldFunc_Sum, 0, YdrunstatHelp },
+                   { "ydrunmean", FieldFunc_Mean, 0, YdrunstatHelp },
+                   { "ydrunavg", FieldFunc_Avg, 0, YdrunstatHelp },
+                   { "ydrunstd", FieldFunc_Std, 0, YdrunstatHelp },
+                   { "ydrunstd1", FieldFunc_Std1, 0, YdrunstatHelp },
+                   { "ydrunvar", FieldFunc_Var, 0, YdrunstatHelp },
+                   { "ydrunvar1", FieldFunc_Var1, 0, YdrunstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ydrunstat> registration = RegisterEntry<Ydrunstat>(module);
 
-class ModuleYdrunstat
-{
 private:
   int operfunc;
 
@@ -165,11 +169,8 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -322,7 +323,7 @@ public:
 
                 auto &rvars1 = vars1[ndates - 1][varID][levelID];
 
-                streamReadRecord(cdiStream, rvars1.vec_d.data(), &rvars1.nmiss);
+                streamReadRecord(cdiStream, rvars1.vec_d.data(), &rvars1.numMissVals);
 
                 if (lvarstd)
                   {
@@ -391,18 +392,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Ydrunstat(void *process)
-{
-  ModuleYdrunstat ydrunstat;
-  ydrunstat.init(process);
-  ydrunstat.run();
-  ydrunstat.close();
-
-  return nullptr;
-}
diff --git a/src/Yeararith.cc b/src/Yeararith.cc
index 9ab2eb69e6dc245046dd0e5d336deeec08219364..c6901503010d83f875e3259019fc82a890121b55 100644
--- a/src/Yeararith.cc
+++ b/src/Yeararith.cc
@@ -23,19 +23,22 @@
 #include "process_int.h"
 #include "field_functions.h"
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("yearadd",  FieldFunc_Add, 0, nullptr);
-  cdo_operator_add("yearsub",  FieldFunc_Sub, 0, nullptr);
-  cdo_operator_add("yearmul",  FieldFunc_Mul, 0, nullptr);
-  cdo_operator_add("yeardiv",  FieldFunc_Div, 0, nullptr);
-  // clang-format on
-}
-
-class ModuleYearArith
+class Yeararith : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Yeararith",
+    .operators = { { "yearadd", FieldFunc_Add, 0, YeararithHelp },
+                   { "yearsub", FieldFunc_Sub, 0, YeararithHelp },
+                   { "yearmul", FieldFunc_Mul, 0, YeararithHelp },
+                   { "yeardiv", FieldFunc_Div, 0, YeararithHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Yeararith> registration = RegisterEntry<Yeararith>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -52,11 +55,8 @@ class ModuleYearArith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -156,18 +156,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Yeararith(void *process)
-{
-  ModuleYearArith yeararith;
-  yeararith.init(process);
-  yeararith.run();
-  yeararith.close();
-
-  return nullptr;
-}
diff --git a/src/Yearmonstat.cc b/src/Yearmonstat.cc
index 4a82f602afd5a67171a7a8019fa96fd86895b72a..d7205917507bbed4af0faf42d5461e1f4a5b891a 100644
--- a/src/Yearmonstat.cc
+++ b/src/Yearmonstat.cc
@@ -22,8 +22,20 @@
 #include "printinfo.h"
 #include "field_functions.h"
 
-class ModuleYearMonStat
+class Yearmonstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Yearmonstat",
+    .operators = { { "yearmonmean", FieldFunc_Mean, 0, YearmonstatHelp },
+                   { "yearmonavg", FieldFunc_Avg, 0, YearmonstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Yearmonstat> registration = RegisterEntry<Yearmonstat>(module);
 
 private:
   CdoStreamID streamID1;
@@ -56,14 +68,10 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
 
-    cdo_initialize(process);
-
     // clang-format off
-    cdo_operator_add("yearmonmean",  FieldFunc_Mean, 0, nullptr);
-    cdo_operator_add("yearmonavg",   FieldFunc_Avg,  0, nullptr);
     // clang-format on
 
     auto operatorID = cdo_operator_id();
@@ -156,7 +164,7 @@ public:
 
                     fieldc_mul(rvars1, dpm);
 
-                    if (rvars1.nmiss || !rsamp1.empty())
+                    if (rvars1.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                         field2_vinit(rsamp1, rvars1, dpm);
@@ -169,7 +177,7 @@ public:
 
                     fieldc_mul(field, dpm);
 
-                    if (field.nmiss || !rsamp1.empty())
+                    if (field.numMissVals || !rsamp1.empty())
                       {
                         if (rsamp1.empty()) rsamp1.resize(rvars1.size, dsets);
                         field2_vincr(rsamp1, field, dpm);
@@ -228,18 +236,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Yearmonstat(void *process)
-{
-  ModuleYearMonStat yearmonstat;
-  yearmonstat.init(process);
-  yearmonstat.run();
-  yearmonstat.close();
-
-  return nullptr;
-}
diff --git a/src/Yhourarith.cc b/src/Yhourarith.cc
index e00077e5f50b3f33bc8f2dabaf37ea30c2b2f613..5a4dc590ee358b94ec487d9f2a3c129420d2c913 100644
--- a/src/Yhourarith.cc
+++ b/src/Yhourarith.cc
@@ -35,17 +35,22 @@ getHourOfYearIndex(const CdiDateTime &vDateTime)
   return houroy;
 }
 
-static void
-add_operators(void)
-{
-  cdo_operator_add("yhouradd", FieldFunc_Add, 0, nullptr);
-  cdo_operator_add("yhoursub", FieldFunc_Sub, 0, nullptr);
-  cdo_operator_add("yhourmul", FieldFunc_Mul, 0, nullptr);
-  cdo_operator_add("yhourdiv", FieldFunc_Div, 0, nullptr);
-}
-
-class ModuleYhourArith
+class Yhourarith : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Yhourarith",
+    .operators = { { "yhouradd", FieldFunc_Add, 0, YhourarithHelp },
+                   { "yhoursub", FieldFunc_Sub, 0, YhourarithHelp },
+                   { "yhourmul", FieldFunc_Mul, 0, YhourarithHelp },
+                   { "yhourdiv", FieldFunc_Div, 0, YhourarithHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Yhourarith> registration = RegisterEntry<Yhourarith>(module);
   int operfunc;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -63,11 +68,8 @@ class ModuleYhourArith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -157,18 +159,5 @@ public:
 
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Yhourarith(void *process)
-{
-  ModuleYhourArith yhourarith;
-  yhourarith.init(process);
-  yhourarith.run();
-  yhourarith.close();
-
-  return nullptr;
-}
diff --git a/src/Yhourstat.cc b/src/Yhourstat.cc
index d9e72f505c54ff12f484abeb0dfb71626253dbaa..42db06594a7fc35dcd4e0a3aea1c8c3f07660d8f 100644
--- a/src/Yhourstat.cc
+++ b/src/Yhourstat.cc
@@ -39,36 +39,41 @@
 #include "printinfo.h"
 #include "field_functions.h"
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("yhourrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("yhourmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("yhourmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("yhoursum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("yhourmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("yhouravg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("yhourvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("yhourvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("yhourstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("yhourstd1",  FieldFunc_Std1,  0, nullptr);
-
-  cdo_operator_add("dhourrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("dhourmin",   FieldFunc_Min,   1, nullptr);
-  cdo_operator_add("dhourmax",   FieldFunc_Max,   1, nullptr);
-  cdo_operator_add("dhoursum",   FieldFunc_Sum,   1, nullptr);
-  cdo_operator_add("dhourmean",  FieldFunc_Mean,  1, nullptr);
-  cdo_operator_add("dhouravg",   FieldFunc_Avg,   1, nullptr);
-  cdo_operator_add("dhourvar",   FieldFunc_Var,   1, nullptr);
-  cdo_operator_add("dhourvar1",  FieldFunc_Var1,  1, nullptr);
-  cdo_operator_add("dhourstd",   FieldFunc_Std,   1, nullptr);
-  cdo_operator_add("dhourstd1",  FieldFunc_Std1,  1, nullptr);
-  // clang-format on
-}
-
-class ModuleYhourStat
+class Yhourstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Yhourstat",
+    // clang-format off
+    .operators = { { "yhourrange", FieldFunc_Range, 0, YhourstatHelp },
+                   { "yhourmin", FieldFunc_Min, 0, YhourstatHelp },
+                   { "yhourmax", FieldFunc_Max, 0, YhourstatHelp },
+                   { "yhoursum", FieldFunc_Sum, 0, YhourstatHelp },
+                   { "yhourmean", FieldFunc_Mean, 0, YhourstatHelp },
+                   { "yhouravg", FieldFunc_Avg, 0, YhourstatHelp },
+                   { "yhourstd", FieldFunc_Std, 0, YhourstatHelp },
+                   { "yhourstd1", FieldFunc_Std1, 0, YhourstatHelp },
+                   { "yhourvar", FieldFunc_Var, 0, YhourstatHelp },
+                   { "yhourvar1", FieldFunc_Var1, 0, YhourstatHelp },
+                   { "dhourrange", FieldFunc_Range, 0, DhourstatHelp },
+                   { "dhourmin", FieldFunc_Min, 1, DhourstatHelp },
+                   { "dhourmax", FieldFunc_Max, 1, DhourstatHelp },
+                   { "dhoursum", FieldFunc_Sum, 1, DhourstatHelp },
+                   { "dhourmean", FieldFunc_Mean, 1, DhourstatHelp },
+                   { "dhouravg", FieldFunc_Avg, 1, DhourstatHelp },
+                   { "dhourstd", FieldFunc_Std, 1, DhourstatHelp },
+                   { "dhourstd1", FieldFunc_Std1, 1, DhourstatHelp },
+                   { "dhourvar", FieldFunc_Var, 1, DhourstatHelp },
+                   { "dhourvar1", FieldFunc_Var1, 1, DhourstatHelp } },
+    // clang-format on
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Yhourstat> registration = RegisterEntry<Yhourstat>(module);
+
 private:
   int MaxHours;
 
@@ -104,14 +109,10 @@ private:
 
 public:
   void
-  init(void *process)
+  init()
   {
     constexpr auto timestatDate{ TimeStat::LAST };
 
-    cdo_initialize(process);
-
-    addOperators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -213,11 +214,11 @@ public:
                 cdo_read_record(streamID1, rvars1);
                 if (lrange)
                   {
-                    vars2[hourot][varID][levelID].nmiss = rvars1.nmiss;
+                    vars2[hourot][varID][levelID].numMissVals = rvars1.numMissVals;
                     vars2[hourot][varID][levelID].vec_d = rvars1.vec_d;
                   }
 
-                if (rvars1.nmiss || !rsamp1.empty())
+                if (rvars1.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                     field2_vinit(rsamp1, rvars1);
@@ -228,7 +229,7 @@ public:
                 field.init(var);
                 cdo_read_record(streamID1, field);
 
-                if (field.nmiss || !rsamp1.empty())
+                if (field.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                     field2_vincr(rsamp1, field);
@@ -281,10 +282,7 @@ public:
                   else
                     fieldc_stdvar_func(rvars1, vars2[hourot][varID][levelID], numSets, divisor);
                 }
-              else if (lrange)
-                {
-                  field2_sub(rvars1, vars2[hourot][varID][levelID]);
-                }
+              else if (lrange) { field2_sub(rvars1, vars2[hourot][varID][levelID]); }
             }
 
           dtlist[hourot].stat_taxis_def_timestep(taxisID2, hourot_numSets[hourot]);
@@ -310,18 +308,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Yhourstat(void *process)
-{
-  ModuleYhourStat yhourstat;
-  yhourstat.init(process);
-  yhourstat.run();
-  yhourstat.close();
-
-  return nullptr;
-}
diff --git a/src/Ymonarith.cc b/src/Ymonarith.cc
index c251dc2a23e4733f54d31dbb105e627794d2bcd4..c0040be55f05ca9431a001f22075ca2aa3d14a72 100644
--- a/src/Ymonarith.cc
+++ b/src/Ymonarith.cc
@@ -45,23 +45,26 @@ enum
   SEASONAL
 };
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("ymonadd",  FieldFunc_Add, MONTHLY, nullptr);
-  cdo_operator_add("ymonsub",  FieldFunc_Sub, MONTHLY, nullptr);
-  cdo_operator_add("ymonmul",  FieldFunc_Mul, MONTHLY, nullptr);
-  cdo_operator_add("ymondiv",  FieldFunc_Div, MONTHLY, nullptr);
-  cdo_operator_add("yseasadd", FieldFunc_Add, SEASONAL, nullptr);
-  cdo_operator_add("yseassub", FieldFunc_Sub, SEASONAL, nullptr);
-  cdo_operator_add("yseasmul", FieldFunc_Mul, SEASONAL, nullptr);
-  cdo_operator_add("yseasdiv", FieldFunc_Div, SEASONAL, nullptr);
-  // clang-format on
-}
-
-class ModuleYmonArith
+class Ymonarith : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ymonarith",
+    .operators = { { "ymonadd", FieldFunc_Add, MONTHLY, YmonarithHelp },
+                   { "ymonsub", FieldFunc_Sub, MONTHLY, YmonarithHelp },
+                   { "ymonmul", FieldFunc_Mul, MONTHLY, YmonarithHelp },
+                   { "ymondiv", FieldFunc_Div, MONTHLY, YmonarithHelp },
+                   { "yseasadd", FieldFunc_Add, SEASONAL, YseasarithHelp },
+                   { "yseassub", FieldFunc_Sub, SEASONAL, YseasarithHelp },
+                   { "yseasmul", FieldFunc_Mul, SEASONAL, YseasarithHelp },
+                   { "yseasdiv", FieldFunc_Div, SEASONAL, YseasarithHelp } },
+    .aliases = { { "anomaly", "ymonsub" } },
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ymonarith> registration = RegisterEntry<Ymonarith>(module);
   int operfunc;
   int opertype;
 
@@ -161,11 +164,8 @@ class ModuleYmonArith
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     opertype = cdo_operator_f2(operatorID);
@@ -210,17 +210,5 @@ public:
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Ymonarith(void *process)
-{
-  ModuleYmonArith ymonarith;
-  ymonarith.init(process);
-  ymonarith.run();
-  ymonarith.close();
-
-  return nullptr;
-}
diff --git a/src/Ymoncomp.cc b/src/Ymoncomp.cc
index 3d2e95417d683a6624b598018bf2aedbf424e47d..7c867606378d2fb1103605b21e18847040b767db 100644
--- a/src/Ymoncomp.cc
+++ b/src/Ymoncomp.cc
@@ -62,7 +62,7 @@ comp_function(int operFunc, bool hasMissvals, size_t ngp, T1 missval1, T2 missva
 static void
 comp_function(Field &f1, const Field &f2, int operFunc)
 {
-  auto hasMissvals = (f1.nmiss > 0 || f2.nmiss > 0);
+  auto hasMissvals = (f1.numMissVals > 0 || f2.numMissVals > 0);
   if (f1.memType == MemType::Float && f2.memType == MemType::Float)
     comp_function(operFunc, hasMissvals, f1.size, (float) f1.missval, (float) f2.missval, f1.vec_f, f2.vec_f);
   else if (f1.memType == MemType::Float && f2.memType == MemType::Double)
@@ -94,27 +94,30 @@ enum
   SEASONAL
 };
 
-static void
-add_operators(void)
-{
-  // clang-format off
-  cdo_operator_add("ymoneq",  FieldFunc_EQ, MONTHLY, nullptr);
-  cdo_operator_add("ymonne",  FieldFunc_NE, MONTHLY, nullptr);
-  cdo_operator_add("ymonle",  FieldFunc_LE, MONTHLY, nullptr);
-  cdo_operator_add("ymonlt",  FieldFunc_LT, MONTHLY, nullptr);
-  cdo_operator_add("ymonge",  FieldFunc_GE, MONTHLY, nullptr);
-  cdo_operator_add("ymongt",  FieldFunc_GT, MONTHLY, nullptr);
-  cdo_operator_add("yseaseq", FieldFunc_EQ, SEASONAL, nullptr);
-  cdo_operator_add("yseasne", FieldFunc_NE, SEASONAL, nullptr);
-  cdo_operator_add("yseasle", FieldFunc_LE, SEASONAL, nullptr);
-  cdo_operator_add("yseaslt", FieldFunc_LT, SEASONAL, nullptr);
-  cdo_operator_add("yseasge", FieldFunc_GE, SEASONAL, nullptr);
-  cdo_operator_add("yseasgt", FieldFunc_GT, SEASONAL, nullptr);
-  // clang-format on
-}
-
-class ModuleYmonComp
+class Ymoncomp : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ymoncomp",
+    .operators = { { "ymoneq", FieldFunc_EQ, MONTHLY, YmoncompHelp },
+                   { "ymonne", FieldFunc_NE, MONTHLY, YmoncompHelp },
+                   { "ymonle", FieldFunc_LE, MONTHLY, YmoncompHelp },
+                   { "ymonlt", FieldFunc_LT, MONTHLY, YmoncompHelp },
+                   { "ymonge", FieldFunc_GE, MONTHLY, YmoncompHelp },
+                   { "ymongt", FieldFunc_GT, MONTHLY, YmoncompHelp },
+                   { "yseaseq", FieldFunc_EQ, SEASONAL, nullptr},
+                   { "yseasne", FieldFunc_NE, SEASONAL, nullptr},
+                   { "yseasle", FieldFunc_LE, SEASONAL, nullptr},
+                   { "yseaslt", FieldFunc_LT, SEASONAL, nullptr},
+                   { "yseasge", FieldFunc_GE, SEASONAL, nullptr},
+                   { "yseasgt", FieldFunc_GT, SEASONAL, nullptr} },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 2, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ymoncomp> registration = RegisterEntry<Ymoncomp>(module);
   int operfunc;
   int opertype;
 
@@ -214,11 +217,8 @@ class ModuleYmonComp
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -263,17 +263,5 @@ public:
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
-    cdo_finish();
   }
 };
-
-void *
-Ymoncomp(void *process)
-{
-  ModuleYmonComp ymoncomp;
-  ymoncomp.init(process);
-  ymoncomp.run();
-  ymoncomp.close();
-
-  return nullptr;
-}
diff --git a/src/Ymonpctl.cc b/src/Ymonpctl.cc
index 091cf264d91f521920c10d61a3b425741435b685..8586fa91158da9bef3abf06fbb37632515bb349f 100644
--- a/src/Ymonpctl.cc
+++ b/src/Ymonpctl.cc
@@ -25,8 +25,19 @@
 #include "percentiles_hist.h"
 #include "field_functions.h"
 
-class ModuleYmonpctl
+class Ymonpctl : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ymonpctl",
+    .operators = { { "ymonpctl", FieldFunc_Pctl, 0, YmonpctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ymonpctl> registration = RegisterEntry<Ymonpctl>(module);
   constexpr static int MaxMonths = 17;
 
   CdoStreamID streamID1;
@@ -58,10 +69,8 @@ class ModuleYmonpctl
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-    cdo_operator_add("ymonpctl", FieldFunc_Pctl, 0, nullptr);
 
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
@@ -240,18 +249,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Ymonpctl(void *process)
-{
-  ModuleYmonpctl ymonpctl;
-  ymonpctl.init(process);
-  ymonpctl.run();
-  ymonpctl.close();
-
-  return nullptr;
-}
diff --git a/src/Ymonstat.cc b/src/Ymonstat.cc
index e011921460fd1755447c0c3f3db626af3eeaedde..46ef57077ace52981f60a8c4ecf2994a41049cb1 100644
--- a/src/Ymonstat.cc
+++ b/src/Ymonstat.cc
@@ -28,27 +28,28 @@
 #include "printinfo.h"
 #include "field_functions.h"
 
-static void
-addOperators(void)
-{
-  // clang-format off
-  cdo_operator_add("ymonrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("ymonmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("ymonmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("ymonmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("ymonmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("ymonsum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("ymonmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("ymonavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("ymonvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("ymonvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("ymonstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("ymonstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-
-class ModuleYmonstat
+class Ymonstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Ymonstat",
+    .operators = { { "ymonrange", FieldFunc_Range, 0, YmonstatHelp },
+                   { "ymonmin", FieldFunc_Min, 0, YmonstatHelp },
+                   { "ymonmax", FieldFunc_Max, 0, YmonstatHelp },
+                   { "ymonsum", FieldFunc_Sum, 0, YmonstatHelp },
+                   { "ymonmean", FieldFunc_Mean, 0, YmonstatHelp },
+                   { "ymonavg", FieldFunc_Avg, 0, YmonstatHelp },
+                   { "ymonstd", FieldFunc_Std, 0, YmonstatHelp },
+                   { "ymonstd1", FieldFunc_Std1, 0, YmonstatHelp },
+                   { "ymonvar", FieldFunc_Var, 0, YmonstatHelp },
+                   { "ymonvar1", FieldFunc_Var1, 0, YmonstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Ymonstat> registration = RegisterEntry<Ymonstat>(module);
   static const int MaxMonths = 17;
   int month_numSets[MaxMonths] = { 0 };
   int mon[MaxMonths] = { 0 };
@@ -79,14 +80,10 @@ class ModuleYmonstat
 
 public:
   void
-  init(void *process)
+  init()
   {
     constexpr auto timestatDate{ TimeStat::LAST };
 
-    cdo_initialize(process);
-
-    addOperators();
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -177,11 +174,11 @@ public:
                 cdo_read_record(streamID1, rvars1);
                 if (lrange)
                   {
-                    vars2[month][varID][levelID].nmiss = rvars1.nmiss;
+                    vars2[month][varID][levelID].numMissVals = rvars1.numMissVals;
                     vars2[month][varID][levelID].vec_d = rvars1.vec_d;
                   }
 
-                if (rvars1.nmiss || !rsamp1.empty())
+                if (rvars1.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                     field2_vinit(rsamp1, rvars1);
@@ -192,7 +189,7 @@ public:
                 field.init(var);
                 cdo_read_record(streamID1, field);
 
-                if (field.nmiss || !rsamp1.empty())
+                if (field.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                     field2_vincr(rsamp1, field);
@@ -256,10 +253,7 @@ public:
                 else
                   fieldc_stdvar_func(rvars1, vars2[month][varID][levelID], numSets, divisor);
               }
-            else if (lrange)
-              {
-                field2_sub(rvars1, vars2[month][varID][levelID]);
-              }
+            else if (lrange) { field2_sub(rvars1, vars2[month][varID][levelID]); }
           }
 
         dtlists[month].stat_taxis_def_timestep(taxisID2);
@@ -285,18 +279,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Ymonstat(void *process)
-{
-  ModuleYmonstat ymonstat;
-  ymonstat.init(process);
-  ymonstat.run();
-  ymonstat.close();
-
-  return nullptr;
-}
diff --git a/src/Yseaspctl.cc b/src/Yseaspctl.cc
index dfb8c16524daa7c08e3cf71c013482c6eb4e85c6..680d3bcf448d9ba0749abb2295b48e76a2a8bfba 100644
--- a/src/Yseaspctl.cc
+++ b/src/Yseaspctl.cc
@@ -26,8 +26,25 @@
 #include "printinfo.h"
 #include "field_functions.h"
 
-class ModuleYseaspctl
+class Yseaspctl : public Process
 {
+  enum
+  {
+    MaxSeasons = 4
+  };
+  long numSets[MaxSeasons] = { 0 };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Yseaspctl",
+    .operators = { { "yseaspctl", FieldFunc_Pctl, 0, YseaspctlHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 3, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Yseaspctl> registration = RegisterEntry<Yseaspctl>(module);
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -42,8 +59,6 @@ class ModuleYseaspctl
   VarList varList1;
   Field field1, field2;
 
-  enum { MaxSeasons = 4 };
-  long numSets[MaxSeasons] = { 0 };
   CdiDateTime vDateTimes1[MaxSeasons]{};
   CdiDateTime vDateTimes2[MaxSeasons]{};
 
@@ -58,14 +73,10 @@ class ModuleYseaspctl
 
 public:
   void
-  init(void *process)
+  init()
   {
     vars1 = std::vector<bool>(MaxSeasons, false);
 
-    cdo_initialize(process);
-
-    cdo_operator_add("yseaspctl", FieldFunc_Pctl, 0, nullptr);
-
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
 
@@ -241,18 +252,5 @@ public:
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Yseaspctl(void *process)
-{
-  ModuleYseaspctl yseaspctl;
-  yseaspctl.init(process);
-  yseaspctl.run();
-  yseaspctl.close();
-
-  return nullptr;
-}
diff --git a/src/Yseasstat.cc b/src/Yseasstat.cc
index 6a34a1d047f99af713841b25918ecbf79711e88c..e9dfddffe2b539150dc633415d4b3ff6ff3e7a6e 100644
--- a/src/Yseasstat.cc
+++ b/src/Yseasstat.cc
@@ -27,27 +27,34 @@
 #include "process_int.h"
 #include "field_functions.h"
 
-static void
-addOperators(void)
+class Yseasstat : public Process
 {
-  // clang-format off
-  cdo_operator_add("yseasrange", FieldFunc_Range, 0, nullptr);
-  cdo_operator_add("yseasmin",   FieldFunc_Min,   0, nullptr);
-  cdo_operator_add("yseasmax",   FieldFunc_Max,   0, nullptr);
-  cdo_operator_add("yseassum",   FieldFunc_Sum,   0, nullptr);
-  cdo_operator_add("yseasmean",  FieldFunc_Mean,  0, nullptr);
-  cdo_operator_add("yseasavg",   FieldFunc_Avg,   0, nullptr);
-  cdo_operator_add("yseasvar",   FieldFunc_Var,   0, nullptr);
-  cdo_operator_add("yseasvar1",  FieldFunc_Var1,  0, nullptr);
-  cdo_operator_add("yseasstd",   FieldFunc_Std,   0, nullptr);
-  cdo_operator_add("yseasstd1",  FieldFunc_Std1,  0, nullptr);
-  // clang-format on
-}
-
-class ModuleYseasstat
-{
-  enum { MaxSeasons = 4 };
+  enum
+  {
+    MaxSeasons = 4
+  };
   int seas_numSets[MaxSeasons] = { 0 };
+
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Yseasstat",
+    .operators = { { "yseasrange", FieldFunc_Range, 0, YseasstatHelp },
+                   { "yseasmin", FieldFunc_Min, 0, YseasstatHelp },
+                   { "yseasmax", FieldFunc_Max, 0, YseasstatHelp },
+                   { "yseassum", FieldFunc_Sum, 0, YseasstatHelp },
+                   { "yseasmean", FieldFunc_Mean, 0, YseasstatHelp },
+                   { "yseasavg", FieldFunc_Avg, 0, YseasstatHelp },
+                   { "yseasstd", FieldFunc_Std, 0, YseasstatHelp },
+                   { "yseasstd1", FieldFunc_Std1, 0, YseasstatHelp },
+                   { "yseasvar", FieldFunc_Var, 0, YseasstatHelp },
+                   { "yseasvar1", FieldFunc_Var1, 0, YseasstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Yseasstat> registration = RegisterEntry<Yseasstat>(module);
   CdiDateTime vDateTimes[MaxSeasons]{};
   FieldVector2D vars1[MaxSeasons], vars2[MaxSeasons], samp1[MaxSeasons];
   CdoStreamID streamID1;
@@ -75,11 +82,8 @@ class ModuleYseasstat
 
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    addOperators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -161,7 +165,7 @@ public:
                 cdo_read_record(streamID1, rvars1);
                 if (lrange) field_copy(rvars1, vars2[seas][varID][levelID]);
 
-                if (rvars1.nmiss || !rsamp1.empty())
+                if (rvars1.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size);
                     field2_vinit(rsamp1, rvars1);
@@ -172,7 +176,7 @@ public:
                 field.init(var);
                 cdo_read_record(streamID1, field);
 
-                if (field.nmiss || !rsamp1.empty())
+                if (field.numMissVals || !rsamp1.empty())
                   {
                     if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
                     field2_vincr(rsamp1, field);
@@ -251,18 +255,5 @@ public:
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    cdo_finish();
   }
 };
-
-void *
-Yseasstat(void *process)
-{
-  ModuleYseasstat yseasstat;
-  yseasstat.init(process);
-  yseasstat.run();
-  yseasstat.close();
-
-  return nullptr;
-}
diff --git a/src/Zonstat.cc b/src/Zonstat.cc
index f74e6966ac9124449b539cc32f6f593cafa195d5..1afafd1175d36ed96bb0fce4bd86936cc1e62ee0 100644
--- a/src/Zonstat.cc
+++ b/src/Zonstat.cc
@@ -69,8 +69,32 @@ define_reduced_grid(int gridID1, size_t ysize, std::vector<int> &hpReducedPoints
   return gridID;
 }
 
-class ModuleZonStat
+class Zonstat : public Process
 {
+public:
+  using Process::Process;
+  inline static CdoModule module = {
+    .name = "Zonstat",
+    .operators = { { "zonrange", FieldFunc_Range, 0, ZonstatHelp },
+                   { "zonmin", FieldFunc_Min, 0, ZonstatHelp },
+                   { "zonmax", FieldFunc_Max, 0, ZonstatHelp },
+                   { "zonsum", FieldFunc_Sum, 0, ZonstatHelp },
+                   { "zonmean", FieldFunc_Mean, 0, ZonstatHelp },
+                   { "zonavg", FieldFunc_Avg, 0, ZonstatHelp },
+                   { "zonstd", FieldFunc_Std, 0, ZonstatHelp },
+                   { "zonstd1", FieldFunc_Std1, 0, ZonstatHelp },
+                   { "zonvar", FieldFunc_Var, 0, ZonstatHelp },
+                   { "zonvar1", FieldFunc_Var1, 0, ZonstatHelp },
+                   { "zonskew", FieldFunc_Skew, 0, ZonstatHelp },
+                   { "zonkurt", FieldFunc_Kurt, 0, ZonstatHelp },
+                   { "zonmedian", FieldFunc_Median, 0, ZonstatHelp },
+                   { "zonpctl", FieldFunc_Pctl, 0, ZonstatHelp } },
+    .aliases = {},
+    .mode = EXPOSED,     // Module mode: 0:intern 1:extern
+    .number = CDI_REAL,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<Zonstat> registration = RegisterEntry<Zonstat>(module);
   int gridIDdestroy = -1, gridID1 = -1, gridID2 = -1;
   int zongridID = -1;
   int sourceGridIsRegular = true;
@@ -94,34 +118,10 @@ class ModuleZonStat
 
   double pn = 0.0;
 
-  void
-  add_operators(void)
-  {
-    // clang-format off
-    cdo_operator_add("zonmin",    FieldFunc_Min,    0, nullptr);
-    cdo_operator_add("zonmax",    FieldFunc_Max,    0, nullptr);
-    cdo_operator_add("zonrange",  FieldFunc_Range,  0, nullptr);
-    cdo_operator_add("zonsum",    FieldFunc_Sum,    0, nullptr);
-    cdo_operator_add("zonmean",   FieldFunc_Mean,   0, nullptr);
-    cdo_operator_add("zonavg",    FieldFunc_Avg,    0, nullptr);
-    cdo_operator_add("zonvar",    FieldFunc_Var,    0, nullptr);
-    cdo_operator_add("zonvar1",   FieldFunc_Var1,   0, nullptr);
-    cdo_operator_add("zonstd",    FieldFunc_Std,    0, nullptr);
-    cdo_operator_add("zonstd1",   FieldFunc_Std1,   0, nullptr);
-    cdo_operator_add("zonskew",   FieldFunc_Skew,   0, nullptr);
-    cdo_operator_add("zonkurt",   FieldFunc_Kurt,   0, nullptr);
-    cdo_operator_add("zonmedian", FieldFunc_Median, 0, nullptr);
-    cdo_operator_add("zonpctl",   FieldFunc_Pctl,   0, nullptr);
-    // clang-format on
-  }
-
 public:
   void
-  init(void *process)
+  init()
   {
-    cdo_initialize(process);
-
-    add_operators();
 
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -142,10 +142,7 @@ public:
         if (!gridInqYbounds(gridID2, NULL)) cdo_abort("Target grid cell bounds missing!");
         if (gridInqXsize(gridID2) > 1) cdo_abort("Target grid must be zonal!");
       }
-    else
-      {
-        operator_check_argc(0);
-      }
+    else { operator_check_argc(0); }
 
     streamID1 = cdo_open_read(0);
 
@@ -274,10 +271,7 @@ public:
             if (is_healpix_grid(varList1[varID].gridID)) reorder_field(field1, hpRingIndices);
             (operfunc == FieldFunc_Pctl) ? zonal_pctl(field1, field2, pn) : zonal_function(field1, field2, operfunc);
           }
-        else
-          {
-            remap_zonal_mean(remapIndices, remapWeights, field1, field2);
-          }
+        else { remap_zonal_mean(remapIndices, remapWeights, field1, field2); }
 
         cdo_def_record(streamID2, varID, levelID);
         cdo_write_record(streamID2, field2);
@@ -304,18 +298,5 @@ public:
     cdo_stream_close(streamID1);
 
     if (gridIDdestroy != -1) gridDestroy(gridIDdestroy);
-
-    cdo_finish();
   }
 };
-
-void *
-Zonstat(void *process)
-{
-  ModuleZonStat zonstat;
-  zonstat.init(process);
-  zonstat.run();
-  zonstat.close();
-
-  return nullptr;
-}
diff --git a/src/after_dvtrans.cc b/src/after_dvtrans.cc
index 6b0f8f16f798c3edfdca09556106bf4ac3bdd059..d0752c484bc679eb7cbcf667e08372195232df2d 100644
--- a/src/after_dvtrans.cc
+++ b/src/after_dvtrans.cc
@@ -12,7 +12,7 @@
 
 #include "constants.h"
 
-#define SQUARE_RADIUS (-PlanetRadius * PlanetRadius)
+#define SQUARE_RADIUS (-PlanetRadiusDefault * PlanetRadiusDefault)
 
 void
 dv2ps(const double *restrict div, double *restrict pot, long nlev, long ntr)
@@ -220,8 +220,8 @@ geninx(long ntr, double *f, double *g)
           long n2 = n * n;
           if (n)
             {
-              *g++ = -PlanetRadius / n * std::sqrt((double) (n2 - m2) / (double) (4 * n2 - 1));
-              *f++ = -PlanetRadius * m / (double) (n2 + n);
+              *g++ = -PlanetRadiusDefault / n * std::sqrt((double) (n2 - m2) / (double) (4 * n2 - 1));
+              *f++ = -PlanetRadiusDefault * m / (double) (n2 + n);
             }
           else
             {
diff --git a/src/after_sptrans.cc b/src/after_sptrans.cc
index 9fad4adde1fa49818c8632a8419f2fba09d32964..fa2d5c4af3f528a8ecc9213a5c746eabe8726c60 100644
--- a/src/after_sptrans.cc
+++ b/src/after_sptrans.cc
@@ -25,17 +25,17 @@ phcs(bool needHnm, double *pnm, double *hnm, long waves, double pmu, double *zte
   double zcospar, zsinpar, zcosfak, zsinfak;
   double zq, zwm2, zw, zwq, zq2m1, zwm2q2, z2q2, zcnm, zdnm, zenm;
 
-  const auto twowaves = waves << 1;
+  auto twowaves = waves << 1;
 
-  const auto zcos2 = std::sqrt(1.0 - pmu * pmu);
-  const auto lat = std::acos(pmu);
+  auto zcos2 = std::sqrt(1.0 - pmu * pmu);
+  auto lat = std::acos(pmu);
   auto zan = 1.0;
 
   ztemp1[0] = 0.5;
 
   for (long jn = 1; jn < twowaves; jn++)
     {
-      const auto zsqp = 1.0 / std::sqrt((double) (jn + jn * jn));
+      auto zsqp = 1.0 / std::sqrt((double) (jn + jn * jn));
       zan *= std::sqrt(1.0 - 1.0 / (4 * jn * jn));
 
       zcospar = std::cos(lat * jn);
@@ -122,7 +122,7 @@ after_legini_full(long ntr, long nlat, double *restrict poli, double *restrict p
 {
   auto dimsp = (ntr + 1) * (ntr + 2);
   auto waves = ntr + 1;
-  const auto twowaves = waves << 1;
+  auto twowaves = waves << 1;
 
   Varray<double> gmu(nlat), gwt(nlat);
   gaussian_latitudes(nlat, gmu.data(), gwt.data());
@@ -150,20 +150,20 @@ after_legini_full(long ntr, long nlat, double *restrict poli, double *restrict p
   for (long jgl = 0; jgl < nlat; ++jgl)
     {
 #ifdef _OPENMP
-      const auto ompthID = cdo_omp_get_thread_num();
+      auto ompthID = cdo_omp_get_thread_num();
       auto &pnm = pnm_2[ompthID];
       auto &hnm = hnm_2[ompthID];
       auto &ztemp1 = ztemp1_2[ompthID];
       auto &ztemp2 = ztemp2_2[ompthID];
 #endif
-      const auto gmusq = 1.0 - gmu[jgl] * gmu[jgl];
+      auto gmusq = 1.0 - gmu[jgl] * gmu[jgl];
       coslat[jgl] = std::sqrt(gmusq);
 
       phcs(needHnm, pnm.data(), hnm.data(), waves, gmu[jgl], ztemp1.data(), ztemp2.data());
 
-      const auto zgwt = gwt[jgl];
-      const auto zrafgmusqr = 1.0 / (PlanetRadius * gmusq);
-      const auto zradsqrtgmusqr = 1.0 / (-PlanetRadius * std::sqrt(gmusq));
+      auto zgwt = gwt[jgl];
+      auto zrafgmusqr = 1.0 / (PlanetRadiusDefault * gmusq);
+      auto zradsqrtgmusqr = 1.0 / (-PlanetRadiusDefault * std::sqrt(gmusq));
 
       auto jsp = jgl;
       for (long jm = 0; jm < waves; ++jm)
@@ -182,8 +182,8 @@ after_legini_full(long ntr, long nlat, double *restrict poli, double *restrict p
 static inline void
 sp2fc_kernel(long nlat, const double *pol, const double *sal, double *restrict far, double *restrict fai)
 {
-  const auto sar = sal[0];
-  const auto sai = sal[1];
+  auto sar = sal[0];
+  auto sai = sal[1];
   for (long lat = 0; lat < nlat; lat++)
     {
       far[lat] += pol[lat] * sar;
diff --git a/src/afterburner.h b/src/afterburner.h
index 34fe7d4b78204f1a85c84146c632889ec41fe7f5..57fef16751861460bf27f61ac9e7eddaebba4427 100644
--- a/src/afterburner.h
+++ b/src/afterburner.h
@@ -10,16 +10,15 @@
 #include <string.h>
 #include <stdarg.h>
 
-#include "dmemory.h"
 #include "process_int.h"
 #include "transform.h"
 #include "cdo_task.h"
+#include "varray.h"
 
-#define MaxLevel 1024
+constexpr int MaxLevel = 1024;
+constexpr int MaxCodes = 277;
 
-#define MaxCodes 277
-
-#define S_ECHAM5 1
+constexpr int ECHAM5_Source = 1;
 
 struct Date
 {
@@ -64,13 +63,13 @@ struct AfterControl
   struct Date OldDate;
   struct Date StartDate;
 
+  int numHalfLevels = 0;
   int nvct = 0;
-  double *vct = nullptr;
+  Varray<double> vct;
 
-  int *vertIndex = nullptr;
-  size_t *pnmiss = nullptr;
-  double *p_of_height = nullptr;
-  double *Orography = nullptr;
+  Varray<int> vertIndex;
+  Varray<double> p_of_height;
+  Varray<double> orography;
 
   int Type = 0;
   int unitsel = 0;
@@ -89,14 +88,14 @@ struct AfterControl
   long DimFC = 0, DimGP = 0, DimSP = 0;
   long DimSP_half = 0;
 
-  double *poli = nullptr;
-  double *pold = nullptr;
-  double *pdev = nullptr;
-  double *pol2 = nullptr;
-  double *pol3 = nullptr;
+  Varray <double> poli;
+  Varray <double> pold;
+  Varray <double> pdev;
+  Varray <double> pol2;
+  Varray <double> pol3;
 
-  double *dv2uv_f1 = nullptr;
-  double *dv2uv_f2 = nullptr;
+  Varray <double> dv2uv_f1;
+  Varray <double> dv2uv_f2;
 
   int NumCodesRequest = 0;
 
@@ -105,33 +104,10 @@ struct AfterControl
   int NumLevelRequest = 0;
   double LevelRequest[MaxLevel];
 
-  double *rcoslat = nullptr;
-  double *coslat = nullptr;
-  double *DerivationFactor = nullptr;
-  double *Field = nullptr;
-
-  ~AfterControl()
-  {
-    if (vct) Free(vct);
-    if (vertIndex) Free(vertIndex);
-    if (pnmiss) Free(pnmiss);
-    if (p_of_height) Free(p_of_height);
-    if (Orography) Free(Orography);
-
-    if (poli) Free(poli);
-    if (pold) Free(pold);
-    if (pdev) Free(pdev);
-    if (pol2) Free(pol2);
-    if (pol3) Free(pol3);
-
-    if (dv2uv_f1) Free(dv2uv_f1);
-    if (dv2uv_f2) Free(dv2uv_f2);
-
-    if (rcoslat) Free(rcoslat);
-    if (coslat) Free(coslat);
-    if (DerivationFactor) Free(DerivationFactor);
-    if (Field) Free(Field);
-  }
+  Varray<double> rcoslat;
+  Varray<double> coslat;
+  Varray<double> derivationFactor;
+  Varray<double> varray;
 };
 
 struct Variable
@@ -152,8 +128,8 @@ struct Variable
   int ogridID;
   int izaxisID;
   int ozaxisID;
-  size_t nmiss0;
-  size_t nmiss;
+  size_t numMissVals0;
+  size_t numMissVals;
   double missval;
   double *spectral;
   double *spectral0;
@@ -172,10 +148,10 @@ struct Variable
 #define    LOW_CLOUD   34
 #define    MID_CLOUD   35
 #define    HIH_CLOUD   36
-#define    LOW_WATER   37  /* not used ?   */
-#define    MID_WATER   38  /* not used ?   */
-#define    HIH_WATER   39  /* not used ?   */
-#define    ALL_WATER   40  /* not used ?   */
+#define    LOW_WATER   37  // not used ?
+#define    MID_WATER   38  // not used ?
+#define    HIH_WATER   39  // not used ?
+#define    ALL_WATER   40  // not used ?
 
 #define GEOPOTENTIAL  129
 #define  TEMPERATURE  130
@@ -194,11 +170,10 @@ struct Variable
 #define GEOPOTHEIGHT  156
 #define    RHUMIDITY  157
 
-#define   SW_BOT_CLF  189  /* not used ?   */
-#define   LW_BOT_CLF  190  /* not used ?   */
-#define   SW_TOP_CLF  191  /* not used ?   */
-#define   LW_TOP_CLF  192  /* not used ?   */
-#define  NET_TOP_CLF  193  /* not computed */
+#define   SW_BOT_CLF  189  // not used ?
+#define   LW_BOT_CLF  190  // not used ?
+#define   SW_TOP_CLF  191  // not used ?
+#define   LW_TOP_CLF  192  // not used ?
 
 #define    WINDSPEED  259
 #define       PRECIP  260
@@ -235,10 +210,10 @@ void after_processPL(AfterControl &globs, struct Variable *vars);
 void after_processML(AfterControl &globs, struct Variable *vars);
 
 void after_AnalysisAddRecord(const AfterControl *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID,
-                             size_t nmiss);
+                             size_t numMissVals);
 double *after_get_dataptr(struct Variable *vars, int code, int gridID, int zaxisID, int levelID);
 void after_EchamAddRecord(const AfterControl *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID,
-                          size_t nmiss);
+                          size_t numMissVals);
 
 void after_AnalysisDependencies(struct Variable *vars, int ncodes);
 void after_EchamDependencies(struct Variable *vars, int ncodes, int type, int source);
diff --git a/src/afterburnerlib.cc b/src/afterburnerlib.cc
index 28f995104ccf9d338b1fb2df12440bb0f5bdb978..bc5db67d20dfe2e305fd8bd84f7fd941cec0cc08 100644
--- a/src/afterburnerlib.cc
+++ b/src/afterburnerlib.cc
@@ -21,46 +21,45 @@ FieldName(int code, const char *text)
 }
 
 // Free array space
-static void *
-FreeMemory(void *ptr)
+template <typename T>
+static void
+FreeMemory(T &ptr)
 {
-  Free(ptr);
-  return nullptr;
+  if (ptr)
+    {
+      free(ptr);
+      ptr = nullptr;
+    }
 }
 
 static void
 FreeSpectral(struct Variable *vars)
 {
-  for (int code = MaxCodes - 1; code >= 0; --code)
-    if (vars[code].spectral) vars[code].spectral = (double *) FreeMemory(vars[code].spectral);
+  for (int code = MaxCodes - 1; code >= 0; --code) FreeMemory(vars[code].spectral);
 }
 
 static void
 FreeFourier(struct Variable *vars)
 {
-  for (int code = 0; code < MaxCodes; ++code)
-    if (vars[code].fourier) vars[code].fourier = (double *) FreeMemory(vars[code].fourier);
+  for (int code = 0; code < MaxCodes; ++code) FreeMemory(vars[code].fourier);
 }
 
 static void
 FreeHybrid(struct Variable *vars)
 {
-  for (int code = 0; code < MaxCodes; ++code)
-    if (vars[code].hybrid) vars[code].hybrid = (double *) FreeMemory(vars[code].hybrid);
+  for (int code = 0; code < MaxCodes; ++code) FreeMemory(vars[code].hybrid);
 }
 
 static void
 FreeGrid(struct Variable *vars)
 {
-  for (int code = 0; code < MaxCodes; ++code)
-    if (vars[code].grid) vars[code].grid = (double *) FreeMemory(vars[code].grid);
+  for (int code = 0; code < MaxCodes; ++code) FreeMemory(vars[code].grid);
 }
 
 static void
 FreeSamp(struct Variable *vars)
 {
-  for (int code = 0; code < MaxCodes; ++code)
-    if (vars[code].samp) vars[code].samp = (int *) FreeMemory(vars[code].samp);
+  for (int code = 0; code < MaxCodes; ++code) FreeMemory(vars[code].samp);
 }
 
 // alloc_dp - Allocate space for double array
@@ -71,7 +70,7 @@ alloc_dp(int words, const char *array_name)
 
   if (words > 0)
     {
-      result = (double *) Malloc(words * sizeof(double));
+      result = (double *) malloc(words * sizeof(double));
       if (result == nullptr) cdo_sys_error(array_name, "No Memory!");
     }
 
@@ -101,18 +100,15 @@ VarQuaSum(double *Variance, const double *restrict Sum, int len, int n)
 }
 
 static void
-AddVector(double *dest, const double *src, size_t len, size_t *nmiss, double missval)
+AddVector(double *dest, const double *src, size_t len, size_t *numMissVals, double missval)
 {
-  if (*nmiss)
+  if (*numMissVals)
     {
       array_add_array_mv(len, dest, src, missval);
-      *nmiss = array_num_mv(len, dest, missval);
-      if (*nmiss == 0) *nmiss = 1;
-    }
-  else
-    {
-      array_add_array(len, dest, src);
+      *numMissVals = array_num_mv(len, dest, missval);
+      if (*numMissVals == 0) *numMissVals = 1;
     }
+  else { array_add_array(len, dest, src); }
 }
 
 static void
@@ -128,9 +124,9 @@ Sub2Vectors(double *dest, const double *restrict srcA, const double *restrict sr
 }
 
 static void
-MultVectorScalar(double *dest, const double *restrict src, double factor, size_t len, size_t nmiss, double missval)
+MultVectorScalar(double *dest, const double *restrict src, double factor, size_t len, size_t numMissVals, double missval)
 {
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i) dest[i] = (IS_EQUAL(src[i], missval)) ? missval : src[i] * factor;
     }
@@ -141,16 +137,16 @@ MultVectorScalar(double *dest, const double *restrict src, double factor, size_t
 }
 
 static void
-DivVectorIvector(double *dest, const double *restrict src, const int *samp, size_t len, size_t *nmiss, double missval)
+DivVectorIvector(double *dest, const double *restrict src, const int *samp, size_t len, size_t *numMissVals, double missval)
 {
-  *nmiss = 0;
+  *numMissVals = 0;
 
   for (size_t i = 0; i < len; ++i)
     {
       if (IS_EQUAL(src[i], missval) || samp[i] == 0)
         {
           dest[i] = missval;
-          *nmiss = *nmiss + 1;
+          *numMissVals = *numMissVals + 1;
         }
       else
         dest[i] = src[i] / samp[i];
@@ -178,7 +174,7 @@ after_gp2sp(const AfterControl &globs, struct Variable *vars, int ccode)
         }
 
       var->spectral = alloc_dp(globs.Dim3SP, "gp2sp.spectral");
-      fc2sp(var->fourier, var->spectral, globs.pold, var->hlev, globs.Latitudes, globs.Fouriers, globs.Truncation);
+      fc2sp(var->fourier, var->spectral, globs.pold.data(), var->hlev, globs.Latitudes, globs.Fouriers, globs.Truncation);
     }
 }
 
@@ -190,8 +186,8 @@ after_GP2FC(double *gp, double *fc, long nlat, long nlon, long nlev, long nfc)
 
   if (ifax[9] != nlon)
     {
-      if (trig) Free(trig);
-      trig = (double *) Malloc(nlon * sizeof(double));
+      if (trig) free(trig);
+      trig = (double *) malloc(nlon * sizeof(double));
       int status = fft_set(trig, ifax, nlon);
       if (status < 0) exit(1);
     }
@@ -207,8 +203,8 @@ after_FC2GP(double *fc, double *gp, long nlat, long nlon, long nlev, long nfc)
 
   if (ifax[9] != nlon)
     {
-      if (trig) Free(trig);
-      trig = (double *) Malloc(nlon * sizeof(double));
+      if (trig) free(trig);
+      trig = (double *) malloc(nlon * sizeof(double));
       int status = fft_set(trig, ifax, nlon);
       if (status < 0) exit(1);
     }
@@ -221,9 +217,6 @@ after_FC2GP(double *fc, double *gp, long nlat, long nlon, long nlev, long nfc)
 static void
 sh2rh(int AnalysisData, double *sphum, double *rhum, double *t, int lev, int dimgpout, const double *level, double *fullpresshybrid)
 {
-  int lpi, lfp;
-  double es, qsatr;
-
   /* ***************************************************** */
   /* Define constants for calculation in presence of water */
   /* ***************************************************** */
@@ -253,8 +246,8 @@ sh2rh(int AnalysisData, double *sphum, double *rhum, double *t, int lev, int dim
     {
       for (int i = 0; i < dimgpout; ++i)
         {
-          lpi = lp * dimgpout + i;
-          lfp = (1 - AnalysisData) * lpi + AnalysisData * lp;
+          auto lpi = lp * dimgpout + i;
+          auto lfp = (1 - AnalysisData) * lpi + AnalysisData * lp;
           /*
           if (t[lpi] < C_RTT) {
             RGAM = RGAMS; RBET = RBETS; RALP = RALPS;
@@ -262,9 +255,9 @@ sh2rh(int AnalysisData, double *sphum, double *rhum, double *t, int lev, int dim
             RGAM = RGAMW; RBET = RBETW; RALP = RALPW;
           }
           */
-          es = (std::exp(RALP - RBET / t[lpi] - RGAM * std::log(t[lpi]))) / fullp[lfp];
+          auto es = (std::exp(RALP - RBET / t[lpi] - RGAM * std::log(t[lpi]))) / fullp[lfp];
           // qsat = es / (1. + C_RETV * (1. - es));
-          qsatr = (1. + C_RETV * (1. - es)) / es;
+          auto qsatr = (1. + C_RETV * (1. - es)) / es;
           rhum[lpi] = sphum[lpi] * 100. * qsatr;
         }
     }
@@ -273,9 +266,6 @@ sh2rh(int AnalysisData, double *sphum, double *rhum, double *t, int lev, int dim
 static void
 rh2sh(double *sphum, double *rhum, double *t, int lev, int dimgpout, const double *level)
 {
-  int lpi;
-  double es, qsat;
-
   /* ***************************************************** */
   /* Define constants for calculation in presence of water */
   /* ***************************************************** */
@@ -303,14 +293,14 @@ rh2sh(double *sphum, double *rhum, double *t, int lev, int dimgpout, const doubl
     {
       for (int i = 0; i < dimgpout; ++i)
         {
-          lpi = lp * dimgpout + i;
+          auto lpi = lp * dimgpout + i;
           /*       if (t[lpi] < C_RTT) { */
           /*          RGAM = RGAMS; RBET = RBETS; RALP = RALPS; */
           /*       }  else { */
           /*          RGAM = RGAMW; RBET = RBETW; RALP = RALPW; */
           /*       } */
-          es = (std::exp(RALP - RBET / t[lpi] - RGAM * std::log(t[lpi]))) / level[lp];
-          qsat = es / (1. + C_RETV * (1. - es));
+          auto es = (std::exp(RALP - RBET / t[lpi] - RGAM * std::log(t[lpi]))) / level[lp];
+          auto qsat = es / (1. + C_RETV * (1. - es));
           sphum[lpi] = rhum[lpi] * qsat / 100.;
         }
     }
@@ -334,34 +324,36 @@ after_FCrh2FCsh(const AfterControl &globs, struct Variable *vars)
 
   after_GP2FC(vars[HUMIDITY].grid, vars[HUMIDITY].fourier, globs.Latitudes, globs.Longitudes, vars[HUMIDITY].plev, globs.Fouriers);
 
-  vars[HUMIDITY].grid = (double *) FreeMemory(vars[HUMIDITY].grid);
-  vars[RHUMIDITY].grid = (double *) FreeMemory(vars[RHUMIDITY].grid);
-  vars[TEMPERATURE].grid = (double *) FreeMemory(vars[TEMPERATURE].grid);
+  FreeMemory(vars[HUMIDITY].grid);
+  FreeMemory(vars[RHUMIDITY].grid);
+  FreeMemory(vars[TEMPERATURE].grid);
 }
 
 void
 after_SPuv2SPdv(const AfterControl &globs, struct Variable *vars)
 {
-  double *Div, *DivOut, *Vor, *VorOut;
+  auto numLevels = globs.NumLevelRequest;
+  auto numLats = globs.Latitudes;
 
-  Div = DivOut = vars[DIVERGENCE].spectral;
-  Vor = VorOut = vars[VORTICITY].spectral;
-  auto fieldSize = globs.DimFC * globs.NumLevelRequest;
+  auto Div = vars[DIVERGENCE].spectral;
+  auto Vor = vars[VORTICITY].spectral;
+  auto DivOut = Div;
+  auto VorOut = Vor;
+  auto fieldSize = globs.DimFC * numLevels;
 
-  if (vars[U_WIND].fourier == nullptr) vars[U_WIND].fourier = alloc_dp(fieldSize, "vars[U_WIND].fourier");
-  if (vars[V_WIND].fourier == nullptr) vars[V_WIND].fourier = alloc_dp(fieldSize, "vars[V_WIND].fourier");
+  auto &u_wind = vars[U_WIND];
+  auto &v_wind = vars[V_WIND];
+  if (u_wind.fourier == nullptr) u_wind.fourier = alloc_dp(fieldSize, "u_wind.fourier");
+  if (v_wind.fourier == nullptr) v_wind.fourier = alloc_dp(fieldSize, "v_wind.fourier");
 
-  sp2fc(vars[U_WIND].spectral, vars[U_WIND].fourier, globs.poli, globs.NumLevelRequest, globs.Latitudes, globs.Fouriers,
-        globs.Truncation);
-  sp2fc(vars[V_WIND].spectral, vars[V_WIND].fourier, globs.poli, globs.NumLevelRequest, globs.Latitudes, globs.Fouriers,
-        globs.Truncation);
-  uv2dv(vars[U_WIND].fourier, vars[V_WIND].fourier, Div, Vor, globs.pol2, globs.pol3, globs.NumLevelRequest, globs.Latitudes,
-        globs.Truncation);
+  sp2fc(u_wind.spectral, u_wind.fourier, globs.poli.data(), numLevels, numLats, globs.Fouriers, globs.Truncation);
+  sp2fc(v_wind.spectral, v_wind.fourier, globs.poli.data(), numLevels, numLats, globs.Fouriers, globs.Truncation);
+  uv2dv(u_wind.fourier, v_wind.fourier, Div, Vor, globs.pol2.data(), globs.pol3.data(), numLevels, numLats, globs.Truncation);
 
-  vars[U_WIND].fourier = (double *) FreeMemory(vars[U_WIND].fourier);
-  vars[V_WIND].fourier = (double *) FreeMemory(vars[V_WIND].fourier);
+  FreeMemory(u_wind.fourier);
+  FreeMemory(v_wind.fourier);
 
-  for (int i = 0; i < globs.NumLevelRequest; ++i)
+  for (int i = 0; i < numLevels; ++i)
     {
       sp2sp(Div, globs.Truncation, DivOut, globs.Truncation);
       sp2sp(Vor, globs.Truncation, VorOut, globs.Truncation);
@@ -391,9 +383,9 @@ after_FCsh2FCrh(const AfterControl &globs, struct Variable *vars)
   after_GP2FC(vars[RHUMIDITY].grid, vars[RHUMIDITY].fourier, globs.Latitudes, globs.Longitudes, vars[RHUMIDITY].plev,
               globs.Fouriers);
 
-  vars[HUMIDITY].grid = (double *) FreeMemory(vars[HUMIDITY].grid);
-  vars[RHUMIDITY].grid = (double *) FreeMemory(vars[RHUMIDITY].grid);
-  vars[TEMPERATURE].grid = (double *) FreeMemory(vars[TEMPERATURE].grid);
+  FreeMemory(vars[HUMIDITY].grid);
+  FreeMemory(vars[RHUMIDITY].grid);
+  FreeMemory(vars[TEMPERATURE].grid);
 }
 /* ENDE HUMTEST */
 
@@ -481,8 +473,8 @@ after_processPL(AfterControl &globs, struct Variable *vars)
         vars[U_WIND].spectral = alloc_dp(globs.DimSP * globs.NumLevelRequest, "vars[U_WIND].spectral");
       if (vars[V_WIND].spectral == nullptr)
         vars[V_WIND].spectral = alloc_dp(globs.DimSP * globs.NumLevelRequest, "vars[V_WIND].spectral");
-      dv2uv(vars[DIVERGENCE].spectral, vars[VORTICITY].spectral, vars[U_WIND].spectral, vars[V_WIND].spectral, globs.dv2uv_f1,
-            globs.dv2uv_f2, globs.Truncation, globs.DimSP, globs.NumLevelRequest);
+      dv2uv(vars[DIVERGENCE].spectral, vars[VORTICITY].spectral, vars[U_WIND].spectral, vars[V_WIND].spectral,
+            globs.dv2uv_f1.data(), globs.dv2uv_f2.data(), globs.Truncation, globs.DimSP, globs.NumLevelRequest);
     }
 
   if (vars[VELOPOT].comp)
@@ -544,13 +536,13 @@ after_processPL(AfterControl &globs, struct Variable *vars)
                 auto fieldSize = vars[code].plev * globs.DimFC;
                 vars[code].fourier = alloc_dp(fieldSize, FieldName(code, "fourier"));
               }
-            sp2fc(vars[code].spectral, vars[code].fourier, globs.poli, vars[code].plev, globs.Latitudes, globs.Fouriers,
+            sp2fc(vars[code].spectral, vars[code].fourier, globs.poli.data(), vars[code].plev, globs.Latitudes, globs.Fouriers,
                   globs.Truncation);
           }
       if (vars[U_WIND].needed && vars[U_WIND].fourier)
-        scaluv(vars[U_WIND].fourier, globs.rcoslat, globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
+        scaluv(vars[U_WIND].fourier, globs.rcoslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
       if (vars[V_WIND].needed && vars[V_WIND].fourier)
-        scaluv(vars[V_WIND].fourier, globs.rcoslat, globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
+        scaluv(vars[V_WIND].fourier, globs.rcoslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
 
       /* HUMTEST */
       if (globs.Type < 70 && vars[HUMIDITY].needed && vars[HUMIDITY].fourier == nullptr)
@@ -698,15 +690,15 @@ after_processPL(AfterControl &globs, struct Variable *vars)
           if (globs.MeanCount == 1)
             array_copy(fieldSize, vars[code].grid, vars[code].mean);
           else
-            AddVector(vars[code].mean, vars[code].grid, fieldSize, &vars[code].nmiss, vars[code].missval);
+            AddVector(vars[code].mean, vars[code].grid, fieldSize, &vars[code].numMissVals, vars[code].missval);
 
           if (globs.EndOfInterval)
             {
               if (vars[code].samp == nullptr)
-                MultVectorScalar(vars[code].mean, vars[code].mean, 1.0 / globs.MeanCount, fieldSize, vars[code].nmiss,
+                MultVectorScalar(vars[code].mean, vars[code].mean, 1.0 / globs.MeanCount, fieldSize, vars[code].numMissVals,
                                  vars[code].missval);
               else
-                DivVectorIvector(vars[code].mean, vars[code].mean, vars[code].samp, fieldSize, &vars[code].nmiss,
+                DivVectorIvector(vars[code].mean, vars[code].mean, vars[code].samp, fieldSize, &vars[code].numMissVals,
                                  vars[code].missval);
             }
         }
@@ -751,12 +743,12 @@ after_processPL(AfterControl &globs, struct Variable *vars)
                 if (globs.Mean != 2)
                   {
                     streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                    streamWriteRecord(globs.ostreamID, vars[code].mean + offset, vars[code].nmiss);
+                    streamWriteRecord(globs.ostreamID, vars[code].mean + offset, vars[code].numMissVals);
                   }
                 if (globs.Mean >= 2)
                   {
                     streamDefRecord(globs.ostreamID2, vars[code].ovarID2, k);
-                    streamWriteRecord(globs.ostreamID2, vars[code].variance + offset, vars[code].nmiss);
+                    streamWriteRecord(globs.ostreamID2, vars[code].variance + offset, vars[code].numMissVals);
                   }
               }
           }
@@ -780,7 +772,7 @@ after_processPL(AfterControl &globs, struct Variable *vars)
               {
                 auto offset = k * globs.DimGP;
                 streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                streamWriteRecord(globs.ostreamID, vars[code].grid + offset, vars[code].nmiss);
+                streamWriteRecord(globs.ostreamID, vars[code].grid + offset, vars[code].numMissVals);
               }
           }
 
@@ -790,12 +782,13 @@ after_processPL(AfterControl &globs, struct Variable *vars)
 }
 
 static void
-theta(double *pthetaf, double *pthetah, double *ph, double *ps, double *tf, double *ts, int levels, int dimgp, int dim3gp)
+theta(double *pthetaf, double *pthetah, const double *ph, const double *ps, const double *tf, const double *ts, int levels,
+      int dimgp, int dim3gp)
 {
-  double *thetah = pthetah;
-  double *thetaf = pthetaf;
+  auto thetah = pthetah;
+  auto thetaf = pthetaf;
 
-  double kappa = PlanetRD / C_RCPD;
+  auto kappa = PlanetRD / C_RCPD;
 
   for (int h = 0; h < dimgp; h++) thetah[h] = 0.0;
   thetah += dimgp;
@@ -820,75 +813,67 @@ windSpeed(double *uvspeed, double *u, double *v, int dim3gp)
 }
 
 static void
-Omega(double *omega_in, double *divergence, double *u_wind, double *v_wind, double *halfpress, double *fullpress, double *dpsdx,
-      double *dpsdy, double *vct, int dimgp, int nlev)
+Omega(double *omega_in, const double *divergence, const double *u_wind, const double *v_wind, const double *halfpress,
+      const double *fullpress, double *dpsdx, const double *dpsdy, const double *vct, int dimgp, int nlev)
 {
-  double DeltaHybrid, Cterm, Pterm;
-  double *diver, *halfp, *fullp, *uwind, *vwind;
-  double *omega = omega_in;
-
   // Compute half level part of vertical velocity
 
-  for (int i = 0; i < dimgp; ++i) omega[i] = 0.0;
+  for (int i = 0; i < dimgp; ++i) omega_in[i] = 0.0;
 
   for (int j = 0; j < nlev; ++j)
     {
-      omega = omega_in + j * dimgp;
-      halfp = halfpress + j * dimgp;
-      diver = divergence + j * dimgp;
-      uwind = u_wind + j * dimgp;
-      vwind = v_wind + j * dimgp;
+      auto omega = omega_in + j * dimgp;
+      const auto halfp = halfpress + j * dimgp;
+      const auto diver = divergence + j * dimgp;
+      const auto uwind = u_wind + j * dimgp;
+      const auto vwind = v_wind + j * dimgp;
 
-      DeltaHybrid = vct[nlev + j + 2] - vct[nlev + j + 1];
+      auto deltaHybrid = vct[nlev + j + 2] - vct[nlev + j + 1];
       for (int i = 0; i < dimgp; ++i)
         {
           omega[i + dimgp]
-              = omega[i] - diver[i] * (halfp[i + dimgp] - halfp[i]) - DeltaHybrid * (uwind[i] * dpsdx[i] + vwind[i] * dpsdy[i]);
+              = omega[i] - diver[i] * (halfp[i + dimgp] - halfp[i]) - deltaHybrid * (uwind[i] * dpsdx[i] + vwind[i] * dpsdy[i]);
         }
     }
 
-  /* interpolate to full levels  */
+  // interpolate to full levels
 
   for (int j = 0; j < nlev; ++j)
     {
-      omega = omega_in + j * dimgp;
+      auto omega = omega_in + j * dimgp;
       for (int i = 0; i < dimgp; ++i) omega[i] = 0.5 * (omega[i] + omega[i + dimgp]);
     }
 
-    /* compute full level part of vertical velocity */
+    // compute full level part of vertical velocity
 
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) private(omega, halfp, fullp, uwind, vwind, DeltaHybrid, Cterm, Pterm)
+#pragma omp parallel for default(shared)
 #endif
   for (int j = 0; j < nlev; ++j)
     {
-      omega = omega_in + j * dimgp;
-      halfp = halfpress + j * dimgp;
-      fullp = fullpress + j * dimgp;
-      uwind = u_wind + j * dimgp;
-      vwind = v_wind + j * dimgp;
+      auto omega = omega_in + j * dimgp;
+      const auto halfp = halfpress + j * dimgp;
+      const auto fullp = fullpress + j * dimgp;
+      const auto uwind = u_wind + j * dimgp;
+      const auto vwind = v_wind + j * dimgp;
 
-      DeltaHybrid = vct[nlev + j + 2] - vct[nlev + j + 1];
-      if (std::fabs(DeltaHybrid) > 0)
+      auto deltaHybrid = vct[nlev + j + 2] - vct[nlev + j + 1];
+      if (std::fabs(deltaHybrid) > 0)
         {
-          Cterm = vct[j + 1] * vct[nlev + j + 1] - vct[j] * vct[nlev + j + 2];
+          auto cterm = vct[j + 1] * vct[nlev + j + 1] - vct[j] * vct[nlev + j + 2];
+          auto ctermIsValid = is_not_equal(cterm, 0.0);
           for (int i = 0; i < dimgp; ++i)
             {
-              if (is_not_equal(Cterm, 0.0))
-                Pterm = Cterm / (halfp[i + dimgp] - halfp[i]) * std::log(halfp[i + dimgp] / halfp[i]);
-              else
-                Pterm = 0.0;
-
+              auto pterm = ctermIsValid ? cterm / (halfp[i + dimgp] - halfp[i]) * std::log(halfp[i + dimgp] / halfp[i]) : 0.0;
               omega[i]
-                  += fullp[i] * (uwind[i] * dpsdx[i] + vwind[i] * dpsdy[i]) / (halfp[i + dimgp] - halfp[i]) * (DeltaHybrid + Pterm);
+                  += fullp[i] * (uwind[i] * dpsdx[i] + vwind[i] * dpsdy[i]) / (halfp[i + dimgp] - halfp[i]) * (deltaHybrid + pterm);
             }
         }
     }
 }
 
 void
-geopot_height_halflevel(double *gheight, const double *const ta_fl, const double *const hus_fl, const double *const p_hl, long ngp,
-                        long nlev)
+geopot_height_half(double *gheight, const double *ta_fl, const double *hus_fl, const double *p_hl, long ngp, long nlev)
 {
   // Computes geopotential height on half level
 
@@ -904,11 +889,11 @@ geopot_height_halflevel(double *gheight, const double *const ta_fl, const double
     {
       for (long k = nlev; k > 1; k--)
         {
-          long offset = ngp * (k - 1);
-          double *gh = gheight + offset;
-          const double *const p = p_hl + offset;
-          const double *const ta = ta_fl + offset;
-          const double *const hus = hus_fl + offset;
+          auto offset = ngp * (k - 1);
+          auto gh = gheight + offset;
+          const auto p = p_hl + offset;
+          const auto ta = ta_fl + offset;
+          const auto hus = hus_fl + offset;
           for (long i = 0; i < ngp; ++i)
             gh[i] = gh[i + ngp] + PlanetRD * ta[i] * (1.0 + vtmp * hus[i]) * std::log(p[i + ngp] / p[i]);
         }
@@ -920,10 +905,10 @@ geopot_height_halflevel(double *gheight, const double *const ta_fl, const double
     {
       for (long k = nlev; k > 1; k--)
         {
-          long offset = ngp * (k - 1);
-          double *gh = gheight + offset;
-          const double *const p = p_hl + offset;
-          const double *const ta = ta_fl + offset;
+          auto offset = ngp * (k - 1);
+          auto gh = gheight + offset;
+          const auto p = p_hl + offset;
+          const auto ta = ta_fl + offset;
           for (long i = 0; i < ngp; ++i) gh[i] = gh[i + ngp] + PlanetRD * ta[i] * std::log(p[i + ngp] / p[i]);
         }
 
@@ -936,8 +921,7 @@ geopot_height_halflevel(double *gheight, const double *const ta_fl, const double
 }
 
 void
-geopot_height_fulllevel(double *gheight, const double *const ta_fl, const double *const hus_fl, const double *const p_hl, long ngp,
-                        long nlev)
+geopot_height_full(double *gheight, const double *ta_fl, const double *hus_fl, const double *p_hl, long ngp, long nlev)
 {
   // Computes geopotential height on full level
 
@@ -953,11 +937,11 @@ geopot_height_fulllevel(double *gheight, const double *const ta_fl, const double
     {
       for (long k = nlev; k > 1; k--)
         {
-          long offset = ngp * (k - 1);
-          double *gh = gheight + offset;
-          const double *const p = p_hl + offset;
-          const double *const ta = ta_fl + offset;
-          const double *const hus = hus_fl + offset;
+          auto offset = ngp * (k - 1);
+          auto gh = gheight + offset;
+          const auto p = p_hl + offset;
+          const auto ta = ta_fl + offset;
+          const auto hus = hus_fl + offset;
           for (long i = 0; i < ngp; ++i)
             gh[i] = gh[i + ngp] + PlanetRD * ta[i] * (1.0 + vtmp * hus[i]) * std::log(p[i + ngp] / p[i]);
         }
@@ -969,9 +953,9 @@ geopot_height_fulllevel(double *gheight, const double *const ta_fl, const double
         {
           long offset = ngp * k;
           double *gh = gheight + offset;
-          const double *const p = p_hl + offset;
-          const double *const ta = ta_fl + offset;
-          const double *const hus = hus_fl + offset;
+          const auto p = p_hl + offset;
+          const auto ta = ta_fl + offset;
+          const auto hus = hus_fl + offset;
           for (long i = 0; i < ngp; ++i)
             gh[i] = gh[i + ngp]
                     + PlanetRD * ta[i] * (1.0 + vtmp * hus[i]) * (1.0 - (p[i] / (p[i + ngp] - p[i])) * std::log(p[i + ngp] / p[i]));
@@ -981,10 +965,10 @@ geopot_height_fulllevel(double *gheight, const double *const ta_fl, const double
     {
       for (long k = nlev; k > 1; k--)
         {
-          long offset = ngp * (k - 1);
-          double *gh = gheight + offset;
-          const double *const p = p_hl + offset;
-          const double *const ta = ta_fl + offset;
+          auto offset = ngp * (k - 1);
+          auto gh = gheight + offset;
+          const auto p = p_hl + offset;
+          const auto ta = ta_fl + offset;
           for (long i = 0; i < ngp; ++i) gh[i] = gh[i + ngp] + PlanetRD * ta[i] * std::log(p[i + ngp] / p[i]);
         }
 
@@ -993,10 +977,10 @@ geopot_height_fulllevel(double *gheight, const double *const ta_fl, const double
 
       for (long k = 1; k < nlev; ++k)
         {
-          long offset = ngp * k;
-          double *gh = gheight + offset;
-          const double *const p = p_hl + offset;
-          const double *const ta = ta_fl + offset;
+          auto offset = ngp * k;
+          auto gh = gheight + offset;
+          const auto p = p_hl + offset;
+          const auto ta = ta_fl + offset;
           for (long i = 0; i < ngp; ++i)
             gh[i] = gh[i + ngp] + PlanetRD * ta[i] * (1.0 - (p[i] / (p[i + ngp] - p[i])) * std::log(p[i + ngp] / p[i]));
         }
@@ -1013,7 +997,7 @@ constexpr double SCALESLP = 101325.0;
 /* ======================================== */
 
 void
-LayerWater(double *ww, double *ll, double pmax, double pmin, int DimGP, int HalfLevels, double *vct)
+LayerWater(double *ww, double *ll, double pmax, double pmin, int DimGP, int HalfLevels, const double *vct)
 {
   double pph[MaxLevel]{};
 
@@ -1026,7 +1010,7 @@ LayerWater(double *ww, double *ll, double pmax, double pmin, int DimGP, int Half
     if (pph[k] < pmin) break;
   auto MinLev = k;
 
-  varray_fill(DimGP, ll, 0.0);
+  ranges::fill_n(ll, DimGP, 0.0);
 
   for (k = MaxLev; k <= MinLev; ++k)
     {
@@ -1040,7 +1024,7 @@ LayerWater(double *ww, double *ll, double pmax, double pmin, int DimGP, int Half
 /* ================================================= */
 
 void
-LayerCloud(double *cc, double *ll, double pmax, double pmin, int DimGP, int HalfLevels, double *vct)
+LayerCloud(double *cc, double *ll, double pmax, double pmin, int DimGP, int HalfLevels, const double *vct)
 {
   double pph[MaxLevel]{};
   constexpr double ZEPSEC = 1.0e-12;
@@ -1081,43 +1065,46 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       if (vars[HALF_PRESS].hybrid == nullptr)
         vars[HALF_PRESS].hybrid = alloc_dp(globs.Dim3GP + globs.DimGP, "vars[HALF_PRESS].hybrid");
 
-      vct_to_hybrid_pressure(vars[FULL_PRESS].hybrid, vars[HALF_PRESS].hybrid, globs.vct, vars[PS_PROG].hybrid, globs.NumLevel,
-                             globs.DimGP);
+      vct_to_hybrid_pressure(vars[FULL_PRESS].hybrid, vars[HALF_PRESS].hybrid, globs.vct.data(), vars[PS_PROG].hybrid,
+                             globs.NumLevel, globs.DimGP);
     }
 
-  if (globs.unitsel > 2) vars[FULL_PRESS].hybrid = (double *) FreeMemory(vars[FULL_PRESS].hybrid);
+  if (globs.unitsel > 2) FreeMemory(vars[FULL_PRESS].hybrid);
 
   if (vars[THETAF].needed)
     {
-      vars[THETAF].hlev = globs.NumLevel;
-      vars[THETAF].plev = globs.NumLevelRequest;
-      vars[THETAF].sfit = true;
-      if (vars[THETAF].hybrid == nullptr) vars[THETAF].hybrid = alloc_dp(globs.Dim3GP, "vars[THETAF].hybrid");
+      auto &thetaf = vars[THETAF];
+      thetaf.hlev = globs.NumLevel;
+      thetaf.plev = globs.NumLevelRequest;
+      thetaf.sfit = true;
+      if (thetaf.hybrid == nullptr) thetaf.hybrid = alloc_dp(globs.Dim3GP, "vars[THETAF].hybrid");
       if (vars[THETAH].hybrid == nullptr) vars[THETAH].hybrid = alloc_dp(globs.Dim3GP, "vars[THETAH].hybrid");
-      theta(vars[THETAF].hybrid, vars[THETAH].hybrid, vars[HALF_PRESS].hybrid, vars[PS_PROG].hybrid, vars[TEMPERATURE].hybrid,
+      theta(thetaf.hybrid, vars[THETAH].hybrid, vars[HALF_PRESS].hybrid, vars[PS_PROG].hybrid, vars[TEMPERATURE].hybrid,
             vars[TS].hybrid, globs.NumLevel, globs.DimGP, globs.Dim3GP);
     }
 
   if (vars[GEOPOTHEIGHT].comp)
     {
-      vars[GEOPOTHEIGHT].hlev = globs.NumLevel + 1;
-      vars[GEOPOTHEIGHT].plev = globs.NumLevelRequest;
-      vars[GEOPOTHEIGHT].sfit = true;
-      vars[GEOPOTHEIGHT].hybrid = alloc_dp(globs.Dim3GP + globs.DimGP, "vars[GEOPOTHEIGHT].hybrid");
+      auto &geopotheight = vars[GEOPOTHEIGHT];
+      geopotheight.hlev = globs.NumLevel + 1;
+      geopotheight.plev = globs.NumLevelRequest;
+      geopotheight.sfit = true;
+      geopotheight.hybrid = alloc_dp(globs.Dim3GP + globs.DimGP, "vars[GEOPOTHEIGHT].hybrid");
 
-      array_copy(globs.DimGP, globs.Orography, vars[GEOPOTHEIGHT].hybrid + globs.Dim3GP);
-      geopot_height_fulllevel(vars[GEOPOTHEIGHT].hybrid, vars[TEMPERATURE].hybrid, vars[HUMIDITY].hybrid, vars[HALF_PRESS].hybrid,
-                              globs.DimGP, globs.NumLevel);
+      array_copy(globs.DimGP, globs.orography.data(), geopotheight.hybrid + globs.Dim3GP);
+      geopot_height_half(geopotheight.hybrid, vars[TEMPERATURE].hybrid, vars[HUMIDITY].hybrid, vars[HALF_PRESS].hybrid, globs.DimGP,
+                         globs.NumLevel);
 
       vars[HUMIDITY].needed = vars[HUMIDITY].selected;
     }
   else if (vars[GEOPOTHEIGHT].hybrid && vars[GEOPOTHEIGHT].hlev == globs.NumLevel)
     {
-      vars[GEOPOTHEIGHT].hlev = globs.NumLevel + 1;
-      vars[GEOPOTHEIGHT].sfit = true;
-      vars[GEOPOTHEIGHT].hybrid = (double *) Realloc(vars[GEOPOTHEIGHT].hybrid, (globs.Dim3GP + globs.DimGP) * sizeof(double));
-      array_copy(globs.DimGP, globs.Orography, vars[GEOPOTHEIGHT].hybrid + globs.Dim3GP);
-      for (int i = 0; i < globs.DimGP; ++i) vars[GEOPOTHEIGHT].hybrid[globs.Dim3GP + i] /= PlanetGrav;
+      auto &geopotheight = vars[GEOPOTHEIGHT];
+      geopotheight.hlev = globs.NumLevel + 1;
+      geopotheight.sfit = true;
+      geopotheight.hybrid = (double *) realloc(geopotheight.hybrid, (globs.Dim3GP + globs.DimGP) * sizeof(double));
+      array_copy(globs.DimGP, globs.orography.data(), geopotheight.hybrid + globs.Dim3GP);
+      for (int i = 0; i < globs.DimGP; ++i) geopotheight.hybrid[globs.Dim3GP + i] /= PlanetGrav;
     }
 
   if (vars[DPSDX].needed || vars[DPSDY].needed)
@@ -1129,13 +1116,14 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
 
   if (vars[OMEGA].comp)
     {
-      vars[OMEGA].hlev = globs.NumLevel + 1;
-      vars[OMEGA].plev = globs.NumLevelRequest;
-      vars[OMEGA].sfit = true;
-      vars[OMEGA].hybrid = alloc_dp(globs.Dim3GP + globs.DimGP, "OMEGA.hybrid");
+      auto &omega = vars[OMEGA];
+      omega.hlev = globs.NumLevel + 1;
+      omega.plev = globs.NumLevelRequest;
+      omega.sfit = true;
+      omega.hybrid = alloc_dp(globs.Dim3GP + globs.DimGP, "omega.hybrid");
 
-      Omega(vars[OMEGA].hybrid, vars[DIVERGENCE].hybrid, vars[U_WIND].hybrid, vars[V_WIND].hybrid, vars[HALF_PRESS].hybrid,
-            vars[FULL_PRESS].hybrid, vars[DPSDX].hybrid, vars[DPSDY].hybrid, globs.vct, globs.DimGP, globs.NumLevel);
+      Omega(omega.hybrid, vars[DIVERGENCE].hybrid, vars[U_WIND].hybrid, vars[V_WIND].hybrid, vars[HALF_PRESS].hybrid,
+            vars[FULL_PRESS].hybrid, vars[DPSDX].hybrid, vars[DPSDY].hybrid, globs.vct.data(), globs.DimGP, globs.NumLevel);
 
       vars[DPSDX].needed = vars[DPSDX].selected;
       vars[DPSDY].needed = vars[DPSDY].selected;
@@ -1143,23 +1131,25 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
 
   if (vars[WINDSPEED].comp)
     {
-      vars[WINDSPEED].hlev = globs.NumLevel;
-      vars[WINDSPEED].plev = globs.NumLevelRequest;
-      vars[WINDSPEED].sfit = true;
-      vars[WINDSPEED].hybrid = alloc_dp(globs.Dim3GP, "vars[WINDSPEED].hybrid");
+      auto &windspeed = vars[WINDSPEED];
+      windspeed.hlev = globs.NumLevel;
+      windspeed.plev = globs.NumLevelRequest;
+      windspeed.sfit = true;
+      windspeed.hybrid = alloc_dp(globs.Dim3GP, "windspeed.hybrid");
 
-      windSpeed(vars[WINDSPEED].hybrid, vars[U_WIND].hybrid, vars[V_WIND].hybrid, globs.Dim3GP);
+      windSpeed(windspeed.hybrid, vars[U_WIND].hybrid, vars[V_WIND].hybrid, globs.Dim3GP);
     }
 
   if (vars[RHUMIDITY].comp)
     {
-      vars[RHUMIDITY].hlev = globs.NumLevel;
-      vars[RHUMIDITY].plev = globs.NumLevelRequest;
-      vars[RHUMIDITY].sfit = false;
-      vars[RHUMIDITY].hybrid = alloc_dp(globs.Dim3GP, "vars[RHUMIDITY].hybrid");
+      auto &rhumidity = vars[RHUMIDITY];
+      rhumidity.hlev = globs.NumLevel;
+      rhumidity.plev = globs.NumLevelRequest;
+      rhumidity.sfit = false;
+      rhumidity.hybrid = alloc_dp(globs.Dim3GP, "rhumidity.hybrid");
 
-      sh2rh(globs.AnalysisData, vars[HUMIDITY].hybrid, vars[RHUMIDITY].hybrid, vars[TEMPERATURE].hybrid, globs.NumLevel,
-            globs.DimGP, globs.LevelRequest, vars[FULL_PRESS].hybrid);
+      sh2rh(globs.AnalysisData, vars[HUMIDITY].hybrid, rhumidity.hybrid, vars[TEMPERATURE].hybrid, globs.NumLevel, globs.DimGP,
+            globs.LevelRequest, vars[FULL_PRESS].hybrid);
 
       vars[TEMPERATURE].needed = vars[TEMPERATURE].selected;
       vars[HUMIDITY].needed = vars[HUMIDITY].selected;
@@ -1167,86 +1157,93 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
 
   if (vars[PS].comp)
     {
-      vars[PS].hlev = 1;
-      vars[PS].plev = 1;
-      vars[PS].sfit = true;  // ???
-      vars[PS].hybrid = alloc_dp(globs.DimGP, "vars[PS].hybrid");
-      for (int l = 0; l < globs.DimGP; ++l) vars[PS].hybrid[l] = std::exp(vars[LNPS].hybrid[l]);
+      auto &ps = vars[PS];
+      ps.hlev = 1;
+      ps.plev = 1;
+      ps.sfit = true;  // ???
+      ps.hybrid = alloc_dp(globs.DimGP, "ps.hybrid");
+      for (int l = 0; l < globs.DimGP; ++l) ps.hybrid[l] = std::exp(vars[LNPS].hybrid[l]);
     }
 
   if (vars[SLP].comp)
     {
-      vars[SLP].hlev = 1;
-      vars[SLP].plev = 1;
-      vars[SLP].sfit = true;
-      vars[SLP].hybrid = alloc_dp(globs.DimGP, "vars[SLP].hybrid");
+      auto &slp = vars[SLP];
+      slp.hlev = 1;
+      slp.plev = 1;
+      slp.sfit = true;
+      slp.hybrid = alloc_dp(globs.DimGP, "slp.hybrid");
 
-      extrapolate_P(vars[SLP].hybrid, vars[HALF_PRESS].hybrid + globs.Dim3GP, vars[FULL_PRESS].hybrid + globs.Dim3GP - globs.DimGP,
-                    globs.Orography, vars[TEMPERATURE].hybrid + globs.Dim3GP - globs.DimGP, globs.DimGP);
+      extrapolate_P(slp.hybrid, vars[HALF_PRESS].hybrid + globs.Dim3GP, vars[FULL_PRESS].hybrid + globs.Dim3GP - globs.DimGP,
+                    globs.orography.data(), vars[TEMPERATURE].hybrid + globs.Dim3GP - globs.DimGP, globs.DimGP);
       vars[TEMPERATURE].needed = vars[TEMPERATURE].selected || vars[GEOPOTHEIGHT].selected;
     }
 
   if (vars[PRECIP].comp)
     {
-      vars[PRECIP].hlev = vars[PRECIP].plev = 1;
-      vars[PRECIP].sfit = false;
-      vars[PRECIP].hybrid = alloc_dp(globs.DimGP, "PRECIP.hybrid");
-      Add2Vectors(vars[PRECIP].hybrid, vars[142].hybrid, vars[143].hybrid, globs.DimGP);
+      auto &precip = vars[PRECIP];
+      precip.hlev = precip.plev = 1;
+      precip.sfit = false;
+      precip.hybrid = alloc_dp(globs.DimGP, "precip.hybrid");
+      Add2Vectors(precip.hybrid, vars[142].hybrid, vars[143].hybrid, globs.DimGP);
     }
 
   if (vars[NET_TOP].comp)
     {
-      vars[NET_TOP].hlev = vars[NET_TOP].plev = 1;
-      vars[NET_TOP].sfit = false;
-      vars[NET_TOP].hybrid = alloc_dp(globs.DimGP, "NET_TOP.hybrid");
-      Add2Vectors(vars[NET_TOP].hybrid, vars[178].hybrid, vars[179].hybrid, globs.DimGP);
+      auto &net_top = vars[NET_TOP];
+      net_top.hlev = net_top.plev = 1;
+      net_top.sfit = false;
+      net_top.hybrid = alloc_dp(globs.DimGP, "net_top.hybrid");
+      Add2Vectors(net_top.hybrid, vars[178].hybrid, vars[179].hybrid, globs.DimGP);
     }
 
   if (vars[NET_BOT].comp)
     {
-      vars[NET_BOT].hlev = vars[NET_BOT].plev = 1;
-      vars[NET_BOT].sfit = false;
-      vars[NET_BOT].hybrid = alloc_dp(globs.DimGP, "NET_BOT.hybrid");
-      Add2Vectors(vars[NET_BOT].hybrid, vars[176].hybrid, vars[177].hybrid, globs.DimGP);
+      auto &net_bot = vars[NET_BOT];
+      net_bot.hlev = net_bot.plev = 1;
+      net_bot.sfit = false;
+      net_bot.hybrid = alloc_dp(globs.DimGP, "net_bot.hybrid");
+      Add2Vectors(net_bot.hybrid, vars[176].hybrid, vars[177].hybrid, globs.DimGP);
     }
 
   if (vars[NET_HEAT].comp)
     {
-      vars[NET_HEAT].hlev = vars[NET_HEAT].plev = 1;
-      vars[NET_HEAT].sfit = false;
-      vars[NET_HEAT].hybrid = alloc_dp(globs.DimGP, "NET_HEAT.hybrid");
+      auto &net_heat = vars[NET_HEAT];
+      net_heat.hlev = net_heat.plev = 1;
+      net_heat.sfit = false;
+      net_heat.hybrid = alloc_dp(globs.DimGP, "net_heat.hybrid");
       /*
-      if (Source == S_ECHAM5)
+      if (Source == ECHAM5_Source)
         {
-          MultVectorScalar(vars[NET_HEAT].hybrid, vars[218].hybrid, (-3.345e5), globs.DimGP, vars[218].nmiss, vars[218].missval);
-          Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[176].hybrid, globs.DimGP);
-          Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[177].hybrid, globs.DimGP);
-          Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[146].hybrid, globs.DimGP);
-          Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[147].hybrid, globs.DimGP);
-          Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[206].hybrid, globs.DimGP);
-          Sub2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[208].hybrid, globs.DimGP);
-          Sub2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[209].hybrid, globs.DimGP);
+          MultVectorScalar(net_heat.hybrid, vars[218].hybrid, (-3.345e5), globs.DimGP, vars[218].numMissVals, vars[218].missval);
+          Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[176].hybrid, globs.DimGP);
+          Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[177].hybrid, globs.DimGP);
+          Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[146].hybrid, globs.DimGP);
+          Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[147].hybrid, globs.DimGP);
+          Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[206].hybrid, globs.DimGP);
+          Sub2Vectors(net_heat.hybrid, net_heat.hybrid, vars[208].hybrid, globs.DimGP);
+          Sub2Vectors(net_heat.hybrid, net_heat.hybrid, vars[209].hybrid, globs.DimGP);
         }
       else
       */
       {
-        MultVectorScalar(vars[NET_HEAT].hybrid, vars[218].hybrid, C_TIMES_RHOH2O, globs.DimGP, vars[218].nmiss, vars[218].missval);
-        Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[176].hybrid, globs.DimGP);
-        Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[177].hybrid, globs.DimGP);
-        Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[146].hybrid, globs.DimGP);
-        Add2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[147].hybrid, globs.DimGP);
-        Sub2Vectors(vars[NET_HEAT].hybrid, vars[NET_HEAT].hybrid, vars[220].hybrid, globs.DimGP);
+        MultVectorScalar(net_heat.hybrid, vars[218].hybrid, C_TIMES_RHOH2O, globs.DimGP, vars[218].numMissVals, vars[218].missval);
+        Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[176].hybrid, globs.DimGP);
+        Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[177].hybrid, globs.DimGP);
+        Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[146].hybrid, globs.DimGP);
+        Add2Vectors(net_heat.hybrid, net_heat.hybrid, vars[147].hybrid, globs.DimGP);
+        Sub2Vectors(net_heat.hybrid, net_heat.hybrid, vars[220].hybrid, globs.DimGP);
       }
     }
 
   if (vars[NET_WATER].comp)
     {
-      vars[NET_WATER].hlev = vars[NET_WATER].plev = 1;
-      vars[NET_WATER].sfit = false;
-      vars[NET_WATER].hybrid = alloc_dp(globs.DimGP, "NET_WATER.hybrid");
-      Sub2Vectors(vars[NET_WATER].hybrid, vars[182].hybrid, vars[160].hybrid, globs.DimGP);
-      Add2Vectors(vars[NET_WATER].hybrid, vars[NET_WATER].hybrid, vars[142].hybrid, globs.DimGP);
-      Add2Vectors(vars[NET_WATER].hybrid, vars[NET_WATER].hybrid, vars[143].hybrid, globs.DimGP);
+      auto &net_water = vars[NET_WATER];
+      net_water.hlev = net_water.plev = 1;
+      net_water.sfit = false;
+      net_water.hybrid = alloc_dp(globs.DimGP, "net_water.hybrid");
+      Sub2Vectors(net_water.hybrid, vars[182].hybrid, vars[160].hybrid, globs.DimGP);
+      Add2Vectors(net_water.hybrid, net_water.hybrid, vars[142].hybrid, globs.DimGP);
+      Add2Vectors(net_water.hybrid, net_water.hybrid, vars[143].hybrid, globs.DimGP);
     }
 
   if (vars[LOW_WATER].comp)
@@ -1254,7 +1251,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[LOW_WATER].hlev = vars[LOW_WATER].plev = 1;
       vars[LOW_WATER].sfit = false;
       vars[LOW_WATER].hybrid = alloc_dp(globs.DimGP, "vars[LOW_WATER].hybrid");
-      LayerWater(vars[222].hybrid, vars[LOW_WATER].hybrid, 75000., 101300., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerWater(vars[222].hybrid, vars[LOW_WATER].hybrid, 75000., 101300., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[MID_WATER].comp)
@@ -1262,7 +1259,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[MID_WATER].hlev = vars[MID_WATER].plev = 1;
       vars[MID_WATER].sfit = false;
       vars[MID_WATER].hybrid = alloc_dp(globs.DimGP, "vars[MID_WATER].hybrid");
-      LayerWater(vars[222].hybrid, vars[MID_WATER].hybrid, 46000., 73000., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerWater(vars[222].hybrid, vars[MID_WATER].hybrid, 46000., 73000., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[HIH_WATER].comp)
@@ -1270,7 +1267,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[HIH_WATER].hlev = vars[HIH_WATER].plev = 1;
       vars[HIH_WATER].sfit = false;
       vars[HIH_WATER].hybrid = alloc_dp(globs.DimGP, "vars[HIH_WATER].hybrid");
-      LayerWater(vars[222].hybrid, vars[HIH_WATER].hybrid, 5000., 44000., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerWater(vars[222].hybrid, vars[HIH_WATER].hybrid, 5000., 44000., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[ALL_WATER].comp)
@@ -1278,7 +1275,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[ALL_WATER].hlev = vars[ALL_WATER].plev = 1;
       vars[ALL_WATER].sfit = false;
       vars[ALL_WATER].hybrid = alloc_dp(globs.DimGP, "vars[ALL_WATER].hybrid");
-      LayerWater(vars[222].hybrid, vars[ALL_WATER].hybrid, 5000., 101300., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerWater(vars[222].hybrid, vars[ALL_WATER].hybrid, 5000., 101300., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[LOW_CLOUD].comp)
@@ -1286,7 +1283,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[LOW_CLOUD].hlev = vars[LOW_CLOUD].plev = 1;
       vars[LOW_CLOUD].sfit = false;
       vars[LOW_CLOUD].hybrid = alloc_dp(globs.DimGP, "vars[LOW_CLOUD].hybrid");
-      LayerCloud(vars[223].hybrid, vars[LOW_CLOUD].hybrid, 75000., 101300., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerCloud(vars[223].hybrid, vars[LOW_CLOUD].hybrid, 75000., 101300., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[MID_CLOUD].comp)
@@ -1294,7 +1291,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[MID_CLOUD].hlev = vars[MID_CLOUD].plev = 1;
       vars[MID_CLOUD].sfit = false;
       vars[MID_CLOUD].hybrid = alloc_dp(globs.DimGP, "vars[MID_CLOUD].hybrid");
-      LayerCloud(vars[223].hybrid, vars[MID_CLOUD].hybrid, 46000., 73000., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerCloud(vars[223].hybrid, vars[MID_CLOUD].hybrid, 46000., 73000., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[HIH_CLOUD].comp)
@@ -1302,7 +1299,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
       vars[HIH_CLOUD].hlev = vars[HIH_CLOUD].plev = 1;
       vars[HIH_CLOUD].sfit = false;
       vars[HIH_CLOUD].hybrid = alloc_dp(globs.DimGP, "vars[HIH_CLOUD].hybrid");
-      LayerCloud(vars[223].hybrid, vars[HIH_CLOUD].hybrid, 5000., 44000., globs.DimGP, globs.HalfLevels, globs.vct);
+      LayerCloud(vars[223].hybrid, vars[HIH_CLOUD].hybrid, 5000., 44000., globs.DimGP, globs.HalfLevels, globs.vct.data());
     }
 
   if (vars[SW_CLF].comp)
@@ -1410,7 +1407,7 @@ after_EchamCompGP(const AfterControl &globs, struct Variable *vars)
 }
 
 static void
-Derivate(double field[], double derilam[], long nlevels, long Waves, long Latitudes, double DerivationFactor[])
+Derivate(double field[], double derilam[], long nlevels, long Waves, long Latitudes, const Varray<double> &derivationFactor)
 {
   long i = 0;
   for (long lev = 0; lev < nlevels; lev++)
@@ -1418,12 +1415,12 @@ Derivate(double field[], double derilam[], long nlevels, long Waves, long Latitu
       {
         for (long l = 0; l < Latitudes; ++l)
           {
-            derilam[i] = -n * field[i + Latitudes] * DerivationFactor[l];
+            derilam[i] = -n * field[i + Latitudes] * derivationFactor[l];
             i++;
           }
         for (long l = 0; l < Latitudes; ++l)
           {
-            derilam[i] = n * field[i - Latitudes] * DerivationFactor[l];
+            derilam[i] = n * field[i - Latitudes] * derivationFactor[l];
             i++;
           }
       }
@@ -1434,7 +1431,7 @@ void
 after_processML(AfterControl &globs, struct Variable *vars)
 {
   int leveltype;
-  size_t nmiss;
+  size_t numMissVals;
   double *pressureLevel = nullptr;
 
   globs.MeanCount++;
@@ -1463,8 +1460,8 @@ after_processML(AfterControl &globs, struct Variable *vars)
       if (vars[DIVERGENCE].spectral == nullptr) after_gp2sp(globs, vars, DIVERGENCE);
       if (vars[VORTICITY].spectral == nullptr) after_gp2sp(globs, vars, VORTICITY);
 
-      dv2uv(vars[DIVERGENCE].spectral, vars[VORTICITY].spectral, vars[U_WIND].spectral, vars[V_WIND].spectral, globs.dv2uv_f1,
-            globs.dv2uv_f2, globs.Truncation, globs.DimSP, vars[DIVERGENCE].hlev);
+      dv2uv(vars[DIVERGENCE].spectral, vars[VORTICITY].spectral, vars[U_WIND].spectral, vars[V_WIND].spectral,
+            globs.dv2uv_f1.data(), globs.dv2uv_f2.data(), globs.Truncation, globs.DimSP, vars[DIVERGENCE].hlev);
     }
 
   if (vars[VELOPOT].comp && globs.Type < 30)
@@ -1489,11 +1486,8 @@ after_processML(AfterControl &globs, struct Variable *vars)
       dv2ps(vars[VORTICITY].spectral, vars[STREAM].spectral, vars[VORTICITY].hlev, globs.Truncation);
     }
 
-  if (vars[VORTICITY].spectral && !vars[VORTICITY].needed)
-    vars[VORTICITY].spectral = (double *) FreeMemory(vars[VORTICITY].spectral);
-
-  if (vars[DIVERGENCE].spectral && !vars[DIVERGENCE].needed)
-    vars[DIVERGENCE].spectral = (double *) FreeMemory(vars[DIVERGENCE].spectral);
+  if (!vars[VORTICITY].needed) FreeMemory(vars[VORTICITY].spectral);
+  if (!vars[DIVERGENCE].needed) FreeMemory(vars[DIVERGENCE].spectral);
 
   /* --------------------------- */
   /*  Output of spectral fields  */
@@ -1532,16 +1526,16 @@ after_processML(AfterControl &globs, struct Variable *vars)
               {
                 auto fieldSize = vars[code].hlev * globs.DimFC;
                 vars[code].fourier = alloc_dp(fieldSize, FieldName(code, "fourier"));
-                sp2fc(vars[code].spectral, vars[code].fourier, globs.poli, vars[code].hlev, globs.Latitudes, globs.Fouriers,
+                sp2fc(vars[code].spectral, vars[code].fourier, globs.poli.data(), vars[code].hlev, globs.Latitudes, globs.Fouriers,
                       globs.Truncation);
               }
-            if (code != LNPS) vars[code].spectral = (double *) FreeMemory(vars[code].spectral);
+            if (code != LNPS) FreeMemory(vars[code].spectral);
           }
 
       if (vars[U_WIND].needed && vars[U_WIND].fourier)
-        scaluv(vars[U_WIND].fourier, globs.rcoslat, globs.Latitudes, globs.Fouriers * globs.NumLevel);
+        scaluv(vars[U_WIND].fourier, globs.rcoslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevel);
       if (vars[V_WIND].needed && vars[V_WIND].fourier)
-        scaluv(vars[V_WIND].fourier, globs.rcoslat, globs.Latitudes, globs.Fouriers * globs.NumLevel);
+        scaluv(vars[V_WIND].fourier, globs.rcoslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevel);
 
       if (vars[DPSDX].needed)
         {
@@ -1550,7 +1544,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
           vars[DPSDX].sfit = false;
           vars[DPSDX].fourier = alloc_dp(globs.DimFC, "vars[DPSDX].fourier");
           if (vars[LNPS].fourier == nullptr) after_gp2sp(globs, vars, LNPS);
-          Derivate(vars[LNPS].fourier, vars[DPSDX].fourier, 1, globs.Waves, globs.Latitudes, globs.DerivationFactor);
+          Derivate(vars[LNPS].fourier, vars[DPSDX].fourier, 1, globs.Waves, globs.Latitudes, globs.derivationFactor);
         }
       if (vars[DPSDY].needed)
         {
@@ -1559,7 +1553,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
           vars[DPSDY].sfit = false;
           vars[DPSDY].fourier = alloc_dp(globs.DimFC, "vars[DPSDY].fourier");
           if (vars[LNPS].spectral == nullptr) after_gp2sp(globs, vars, LNPS);
-          sp2fc(vars[LNPS].spectral, vars[DPSDY].fourier, globs.pdev, vars[DPSDY].hlev, globs.Latitudes, globs.Fouriers,
+          sp2fc(vars[LNPS].spectral, vars[DPSDY].fourier, globs.pdev.data(), vars[DPSDY].hlev, globs.Latitudes, globs.Fouriers,
                 globs.Truncation);
         }
     }
@@ -1630,7 +1624,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
                 after_FC2GP(vars[code].fourier, vars[code].hybrid, globs.Latitudes, globs.Longitudes, vars[code].hlev,
                             globs.Fouriers);
               }
-            vars[code].fourier = (double *) FreeMemory(vars[code].fourier);
+            FreeMemory(vars[code].fourier);
           }
 
       if (vars[PS_PROG].comp && vars[PS_PROG].hybrid == nullptr)
@@ -1645,24 +1639,21 @@ after_processML(AfterControl &globs, struct Variable *vars)
               cdo_warning("log surface pressure (code 152) not found - using surface pressure (code 134)!");
               array_copy(globs.DimGP, vars[PS].hybrid, vars[PS_PROG].hybrid);
             }
-          else
-            {
-              afterAbort("surface pressure not found!");
-            }
+          else { afterAbort("surface pressure not found!"); }
         }
       vars[LNPS].needed = vars[LNPS].selected;
 
-      if (globs.Orography == nullptr)
+      if (globs.orography.size() == 0)
         {
-          globs.Orography = alloc_dp(globs.DimGP, "Orography");
+          globs.orography.resize(globs.DimGP);
           if (vars[GEOPOTENTIAL].hybrid)
-            array_copy(globs.DimGP, vars[GEOPOTENTIAL].hybrid, globs.Orography);
+            array_copy(globs.DimGP, vars[GEOPOTENTIAL].hybrid, globs.orography.data());
           else
             {
               if (vars[GEOPOTENTIAL].selected || globs.Type >= 30)
                 {
                   cdo_warning("Orography not found - using zero orography!");
-                  varray_fill(globs.DimGP, globs.Orography, 0.0);
+                  ranges::fill(globs.orography, 0.0);
                 }
             }
         }
@@ -1699,17 +1690,17 @@ after_processML(AfterControl &globs, struct Variable *vars)
                     }
                   else
                     {
-                      AddVector(vars[code].mean, vars[code].hybrid, fieldSize, &vars[code].nmiss, vars[code].missval);
+                      AddVector(vars[code].mean, vars[code].hybrid, fieldSize, &vars[code].numMissVals, vars[code].missval);
                       if (globs.Mean > 1) AddQuaSum(vars[code].variance, vars[code].hybrid, fieldSize);
                     }
 
                   if (globs.EndOfInterval)
                     {
                       if (vars[code].samp == nullptr)
-                        MultVectorScalar(vars[code].hybrid, vars[code].mean, 1.0 / globs.MeanCount, fieldSize, vars[code].nmiss,
-                                         vars[code].missval);
+                        MultVectorScalar(vars[code].hybrid, vars[code].mean, 1.0 / globs.MeanCount, fieldSize,
+                                         vars[code].numMissVals, vars[code].missval);
                       else
-                        DivVectorIvector(vars[code].hybrid, vars[code].mean, vars[code].samp, fieldSize, &vars[code].nmiss,
+                        DivVectorIvector(vars[code].hybrid, vars[code].mean, vars[code].samp, fieldSize, &vars[code].numMissVals,
                                          vars[code].missval);
 
                       if (globs.Mean > 1) VarQuaSum(vars[code].variance, vars[code].hybrid, fieldSize, globs.MeanCount);
@@ -1736,12 +1727,12 @@ after_processML(AfterControl &globs, struct Variable *vars)
                     if (globs.Mean != 2)
                       {
                         streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                        streamWriteRecord(globs.ostreamID, vars[code].hybrid + offset, vars[code].nmiss);
+                        streamWriteRecord(globs.ostreamID, vars[code].hybrid + offset, vars[code].numMissVals);
                       }
                     if (globs.Mean >= 2)
                       {
                         streamDefRecord(globs.ostreamID2, vars[code].ovarID2, k);
-                        streamWriteRecord(globs.ostreamID2, vars[code].variance + offset, vars[code].nmiss);
+                        streamWriteRecord(globs.ostreamID2, vars[code].variance + offset, vars[code].numMissVals);
                       }
                   }
               }
@@ -1759,27 +1750,26 @@ after_processML(AfterControl &globs, struct Variable *vars)
 
   if (globs.Type >= 30)
     {
-      if (globs.vertIndex == nullptr) globs.vertIndex = (int *) Malloc(globs.NumLevelRequest * globs.DimGP * sizeof(int));
+      if (globs.vertIndex.size() == 0) globs.vertIndex.resize(globs.NumLevelRequest * globs.DimGP);
 
       if (globs.unitsel)
         {
-          if (globs.p_of_height == nullptr) globs.p_of_height = alloc_dp(globs.NumLevelRequest, "p_of_height");
-          height_to_pressure(globs.LevelRequest, globs.p_of_height, globs.NumLevelRequest);
-          pressureLevel = globs.p_of_height;
-        }
-      else
-        {
-          pressureLevel = globs.LevelRequest;
+          if (globs.p_of_height.size() == 0) globs.p_of_height.resize(globs.NumLevelRequest);
+          height_to_pressure(globs.LevelRequest, globs.p_of_height.data(), globs.NumLevelRequest);
+          pressureLevel = globs.p_of_height.data();
         }
+      else { pressureLevel = globs.LevelRequest; }
 
-      gen_vert_index(globs.vertIndex, pressureLevel, vars[FULL_PRESS].hybrid, globs.DimGP, globs.NumLevelRequest, globs.NumLevel);
+      gen_vert_index(globs.vertIndex.data(), pressureLevel, vars[FULL_PRESS].hybrid, globs.DimGP, globs.NumLevelRequest,
+                     globs.NumLevel);
 
-      nmiss = 0;
+      numMissVals = 0;
       if (!globs.extrapolate)
         {
-          if (globs.pnmiss == nullptr) globs.pnmiss = (size_t *) Malloc(globs.NumLevelRequest * sizeof(size_t));
-          gen_vert_index_mv(globs.vertIndex, pressureLevel, globs.DimGP, globs.NumLevelRequest, vars[PS_PROG].hybrid, globs.pnmiss);
-          for (int i = 0; i < globs.NumLevelRequest; ++i) nmiss += globs.pnmiss[i];
+          Varray<size_t> pnumMissVals(globs.NumLevelRequest);
+          gen_vert_index_mv(globs.vertIndex.data(), pressureLevel, globs.DimGP, globs.NumLevelRequest, vars[PS_PROG].hybrid,
+                            pnumMissVals.data());
+          for (int i = 0; i < globs.NumLevelRequest; ++i) numMissVals += pnumMissVals[i];
         }
 
       for (int code = 0; code < MaxCodes; ++code)
@@ -1788,7 +1778,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
             leveltype = zaxisInqType(vars[code].izaxisID);
             if (vars[code].hlev == 1 || leveltype != ZAXIS_HYBRID || (vars[code].hlev < globs.NumLevel))
               {
-                if (vars[code].grid) FreeMemory(vars[code].grid);
+                FreeMemory(vars[code].grid);
                 vars[code].grid = vars[code].hybrid;
                 vars[code].hybrid = nullptr;
               }
@@ -1802,16 +1792,17 @@ after_processML(AfterControl &globs, struct Variable *vars)
 
                 if (code == TEMPERATURE)
                   {
-                    vertical_interp_T(globs.Orography, vars[TEMPERATURE].hybrid, vars[TEMPERATURE].grid, vars[FULL_PRESS].hybrid,
-                                      vars[HALF_PRESS].hybrid, globs.vertIndex, pressureLevel, globs.NumLevelRequest, globs.DimGP,
-                                      globs.NumLevel, vars[code].missval);
+                    vertical_interp_T(globs.orography.data(), vars[TEMPERATURE].hybrid, vars[TEMPERATURE].grid,
+                                      vars[FULL_PRESS].hybrid, vars[HALF_PRESS].hybrid, globs.vertIndex.data(), pressureLevel,
+                                      globs.NumLevelRequest, globs.DimGP, globs.NumLevel, vars[code].missval);
                   }
                 else if (code == GEOPOTHEIGHT)
                   {
                     if (vars[TEMPERATURE].hybrid == nullptr) afterAbort("Code  130 not found!");
-                    vertical_interp_Z(globs.Orography, vars[GEOPOTHEIGHT].hybrid, vars[GEOPOTHEIGHT].grid, vars[FULL_PRESS].hybrid,
-                                      vars[HALF_PRESS].hybrid, globs.vertIndex, vars[TEMPERATURE].hybrid, pressureLevel,
-                                      globs.NumLevelRequest, globs.DimGP, globs.NumLevel, vars[code].missval);
+                    vertical_interp_Z(globs.orography.data(), vars[GEOPOTHEIGHT].hybrid, vars[GEOPOTHEIGHT].grid,
+                                      vars[FULL_PRESS].hybrid, vars[HALF_PRESS].hybrid, globs.vertIndex.data(),
+                                      vars[TEMPERATURE].hybrid, pressureLevel, globs.NumLevelRequest, globs.DimGP, globs.NumLevel,
+                                      vars[code].missval);
                   }
                 else
                   {
@@ -1820,20 +1811,20 @@ after_processML(AfterControl &globs, struct Variable *vars)
                     double *hyb_press = vars[FULL_PRESS].hybrid;
                     if (numlevel == (globs.NumLevel + 1)) hyb_press = vars[HALF_PRESS].hybrid;
 
-                    vertical_interp_X(vars[code].hybrid, vars[code].grid, hyb_press, globs.vertIndex, pressureLevel,
+                    vertical_interp_X(vars[code].hybrid, vars[code].grid, hyb_press, globs.vertIndex.data(), pressureLevel,
                                       globs.NumLevelRequest, globs.DimGP, numlevel, vars[code].missval);
                   }
 
-                if (!globs.extrapolate) vars[code].nmiss = nmiss;
+                if (!globs.extrapolate) vars[code].numMissVals = numMissVals;
 
-                if (code != TEMPERATURE) vars[code].hybrid = (double *) FreeMemory(vars[code].hybrid);
+                if (code != TEMPERATURE) FreeMemory(vars[code].hybrid);
               }
           }
     }
 
   vars[TEMPERATURE].needed = vars[TEMPERATURE].selected;
   FreeHybrid(vars);
-  if (vars[HALF_PRESS].hybrid) vars[HALF_PRESS].hybrid = (double *) FreeMemory(vars[HALF_PRESS].hybrid);
+  FreeMemory(vars[HALF_PRESS].hybrid);
 
   /* -------------------------------- */
   /*  Output of pressure level grids  */
@@ -1851,7 +1842,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
                   {
                     auto offset = k * globs.DimGP;
                     streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                    streamWriteRecord(globs.ostreamID, vars[code].grid + offset, vars[code].nmiss);
+                    streamWriteRecord(globs.ostreamID, vars[code].grid + offset, vars[code].numMissVals);
                   }
               }
           }
@@ -1875,15 +1866,15 @@ after_processML(AfterControl &globs, struct Variable *vars)
           if (globs.MeanCount == 1)
             array_copy(fieldSize, vars[code].grid, vars[code].mean);
           else
-            AddVector(vars[code].mean, vars[code].grid, fieldSize, &vars[code].nmiss, vars[code].missval);
+            AddVector(vars[code].mean, vars[code].grid, fieldSize, &vars[code].numMissVals, vars[code].missval);
 
           if (globs.EndOfInterval)
             {
               if (vars[code].samp == nullptr)
-                MultVectorScalar(vars[code].mean, vars[code].mean, 1.0 / globs.MeanCount, fieldSize, vars[code].nmiss,
+                MultVectorScalar(vars[code].mean, vars[code].mean, 1.0 / globs.MeanCount, fieldSize, vars[code].numMissVals,
                                  vars[code].missval);
               else
-                DivVectorIvector(vars[code].mean, vars[code].mean, vars[code].samp, fieldSize, &vars[code].nmiss,
+                DivVectorIvector(vars[code].mean, vars[code].mean, vars[code].samp, fieldSize, &vars[code].numMissVals,
                                  vars[code].missval);
             }
         }
@@ -1930,12 +1921,12 @@ after_processML(AfterControl &globs, struct Variable *vars)
                 if (globs.Mean != 2)
                   {
                     streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                    streamWriteRecord(globs.ostreamID, vars[code].mean + offset, vars[code].nmiss);
+                    streamWriteRecord(globs.ostreamID, vars[code].mean + offset, vars[code].numMissVals);
                   }
                 if (globs.Mean >= 2)
                   {
                     streamDefRecord(globs.ostreamID2, vars[code].ovarID2, k);
-                    streamWriteRecord(globs.ostreamID2, vars[code].variance + offset, vars[code].nmiss);
+                    streamWriteRecord(globs.ostreamID2, vars[code].variance + offset, vars[code].numMissVals);
                   }
               }
           }
@@ -1953,8 +1944,8 @@ after_processML(AfterControl &globs, struct Variable *vars)
     for (int code = 0; code < MaxCodes; ++code)
       if (vars[code].mean)
         {
-          if (vars[code].variance) vars[code].variance = (double *) FreeMemory(vars[code].variance);
-          if (vars[code].grid) vars[code].grid = (double *) FreeMemory(vars[code].grid);
+          FreeMemory(vars[code].variance);
+          FreeMemory(vars[code].grid);
           vars[code].grid = vars[code].mean;
           vars[code].mean = nullptr;
         }
@@ -1968,7 +1959,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
       for (int code = 0; code < MaxCodes; ++code)
         if (vars[code].needed && vars[code].grid && (vars[code].sfit || globs.Type < 70))
           {
-            if (vars[code].nmiss) afterAbort("Missing values for code %d unsupported with TYPE > 30!", code);
+            if (vars[code].numMissVals) afterAbort("Missing values for code %d unsupported with TYPE > 30!", code);
 
             if (vars[code].fourier == nullptr)
               {
@@ -1978,12 +1969,12 @@ after_processML(AfterControl &globs, struct Variable *vars)
 
             after_GP2FC(vars[code].grid, vars[code].fourier, globs.Latitudes, globs.Longitudes, vars[code].plev, globs.Fouriers);
 
-            if (vars[code].grid && (vars[code].sfit || globs.Type < 70)) vars[code].grid = (double *) FreeMemory(vars[code].grid);
+            if (vars[code].sfit || globs.Type < 70) FreeMemory(vars[code].grid);
           }
     }
 
   for (int code = 0; code < MaxCodes; ++code)
-    if (vars[code].grid && (vars[code].sfit || globs.Type < 70)) vars[code].grid = (double *) FreeMemory(vars[code].grid);
+    if (vars[code].sfit || globs.Type < 70) FreeMemory(vars[code].grid);
 
   /* -------------------------- */
   /*  Output of fourier fields  */
@@ -2001,7 +1992,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
               {
                 auto offset = k * globs.DimFC;
                 streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                streamWriteRecord(globs.ostreamID, vars[code].fourier + offset, vars[code].nmiss);
+                streamWriteRecord(globs.ostreamID, vars[code].fourier + offset, vars[code].numMissVals);
               }
           }
 
@@ -2025,7 +2016,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
               {
                 auto offset = k * globs.DimFC;
                 streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                streamWriteRecord(globs.ostreamID, vars[code].fourier + offset, vars[code].nmiss);
+                streamWriteRecord(globs.ostreamID, vars[code].fourier + offset, vars[code].numMissVals);
               }
           }
 
@@ -2040,9 +2031,9 @@ after_processML(AfterControl &globs, struct Variable *vars)
   if (globs.Type >= 50)
     {
       if (vars[U_WIND].needed && vars[U_WIND].fourier)
-        scaluv(vars[U_WIND].fourier, globs.coslat, globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
+        scaluv(vars[U_WIND].fourier, globs.coslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
       if (vars[V_WIND].needed && vars[V_WIND].fourier)
-        scaluv(vars[V_WIND].fourier, globs.coslat, globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
+        scaluv(vars[V_WIND].fourier, globs.coslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
 
       for (int code = 0; code < MaxCodes; ++code)
         if (vars[code].needed && vars[code].fourier)
@@ -2053,7 +2044,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
                 vars[code].spectral = alloc_dp(fieldSize, FieldName(code, "spectral"));
               }
 
-            fc2sp(vars[code].fourier, vars[code].spectral, globs.pold, vars[code].plev, globs.Latitudes, globs.Fouriers,
+            fc2sp(vars[code].fourier, vars[code].spectral, globs.pold.data(), vars[code].plev, globs.Latitudes, globs.Fouriers,
                   globs.Truncation);
           }
 
@@ -2065,8 +2056,8 @@ after_processML(AfterControl &globs, struct Variable *vars)
             vars[VORTICITY].spectral = alloc_dp(globs.DimSP * globs.NumLevelRequest, "vars[VORTICITY].spectral");
           if ((vars[U_WIND].fourier == 0 || vars[V_WIND].fourier == 0) && globs.NumLevelRequest)
             afterAbort("uwind or vwind missing!");
-          uv2dv(vars[U_WIND].fourier, vars[V_WIND].fourier, vars[DIVERGENCE].spectral, vars[VORTICITY].spectral, globs.pol2,
-                globs.pol3, globs.NumLevelRequest, globs.Latitudes, globs.Truncation);
+          uv2dv(vars[U_WIND].fourier, vars[V_WIND].fourier, vars[DIVERGENCE].spectral, vars[VORTICITY].spectral, globs.pol2.data(),
+                globs.pol3.data(), globs.NumLevelRequest, globs.Latitudes, globs.Truncation);
         }
 
       if (vars[VELOPOT].needed)
@@ -2091,7 +2082,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
     }
 
   for (int code = 0; code < MaxCodes; ++code)
-    if (vars[code].fourier && (vars[code].sfit || globs.Type < 61)) vars[code].fourier = (double *) FreeMemory(vars[code].fourier);
+    if (vars[code].sfit || globs.Type < 61) FreeMemory(vars[code].fourier);
 
   /* --------------------------- */
   /*  Output of spectral fields  */
@@ -2129,13 +2120,13 @@ after_processML(AfterControl &globs, struct Variable *vars)
                 auto fieldSize = vars[code].plev * globs.DimFC;
                 vars[code].fourier = alloc_dp(fieldSize, FieldName(code, "fourier"));
               }
-            sp2fc(vars[code].spectral, vars[code].fourier, globs.poli, vars[code].plev, globs.Latitudes, globs.Fouriers,
+            sp2fc(vars[code].spectral, vars[code].fourier, globs.poli.data(), vars[code].plev, globs.Latitudes, globs.Fouriers,
                   globs.Truncation);
           }
       if (vars[U_WIND].needed && vars[U_WIND].fourier)
-        scaluv(vars[U_WIND].fourier, globs.rcoslat, globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
+        scaluv(vars[U_WIND].fourier, globs.rcoslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
       if (vars[V_WIND].needed && vars[V_WIND].fourier)
-        scaluv(vars[V_WIND].fourier, globs.rcoslat, globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
+        scaluv(vars[V_WIND].fourier, globs.rcoslat.data(), globs.Latitudes, globs.Fouriers * globs.NumLevelRequest);
     }
 
   FreeSpectral(vars);
@@ -2180,7 +2171,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
               {
                 auto offset = k * globs.DimFC;
                 streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                streamWriteRecord(globs.ostreamID, vars[code].fourier + offset, vars[code].nmiss);
+                streamWriteRecord(globs.ostreamID, vars[code].fourier + offset, vars[code].numMissVals);
               }
           }
 
@@ -2220,7 +2211,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
               {
                 auto offset = k * globs.DimGP;
                 streamDefRecord(globs.ostreamID, vars[code].ovarID, k);
-                streamWriteRecord(globs.ostreamID, vars[code].grid + offset, vars[code].nmiss);
+                streamWriteRecord(globs.ostreamID, vars[code].grid + offset, vars[code].numMissVals);
               }
           }
 
@@ -2232,7 +2223,7 @@ after_processML(AfterControl &globs, struct Variable *vars)
 
 void
 after_AnalysisAddRecord(const AfterControl *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID,
-                        size_t nmiss)
+                        size_t numMissVals)
 {
   int truncation;
 
@@ -2243,7 +2234,7 @@ after_AnalysisAddRecord(const AfterControl *globs, struct Variable *vars, int co
   auto dataSize = gridSize * nlevel;
   auto dataOffset = gridSize * levelID;
 
-  vars[code].nmiss0 += nmiss;
+  vars[code].numMissVals0 += numMissVals;
 
   if (gridtype == GRID_SPECTRAL)
     {
@@ -2257,13 +2248,13 @@ after_AnalysisAddRecord(const AfterControl *globs, struct Variable *vars, int co
               auto fieldSize = globs->Dim3SP;
               if (vars[code].spectral0 == nullptr) vars[code].spectral0 = alloc_dp(fieldSize, FieldName(code, "spectral"));
               truncation = gridInqTrunc(gridID);
-              sp2sp(globs->Field, truncation, vars[code].spectral0 + levelID * globs->DimSP, globs->Truncation);
+              sp2sp(globs->varray.data(), truncation, vars[code].spectral0 + levelID * globs->DimSP, globs->Truncation);
             }
           else
             {
               auto fieldSize = globs->Dim3SP;
               if (vars[code].spectral0 == nullptr) vars[code].spectral0 = alloc_dp(fieldSize, FieldName(code, "spectral"));
-              array_copy(globs->DimSP, globs->Field, vars[code].spectral0 + levelID * globs->DimSP);
+              array_copy(globs->DimSP, globs->varray.data(), vars[code].spectral0 + levelID * globs->DimSP);
             }
         }
       else
@@ -2278,7 +2269,7 @@ after_AnalysisAddRecord(const AfterControl *globs, struct Variable *vars, int co
           vars[code].hlev = globs->NumLevelRequest;
           vars[code].plev = globs->NumLevelRequest;
           if (vars[code].grid0 == nullptr) vars[code].grid0 = alloc_dp(fieldSize, FieldName(code, "grid0"));
-          array_copy(globs->DimGP, globs->Field, vars[code].grid0 + levelID * globs->DimGP);
+          array_copy(globs->DimGP, globs->varray.data(), vars[code].grid0 + levelID * globs->DimGP);
         }
       else
         {
@@ -2287,19 +2278,19 @@ after_AnalysisAddRecord(const AfterControl *globs, struct Variable *vars, int co
           vars[code].hlev = 1;
           vars[code].plev = 1;
           if (vars[code].grid0 == nullptr) vars[code].grid0 = alloc_dp(fieldSize, FieldName(code, "grid0"));
-          array_copy(globs->DimGP, globs->Field, vars[code].grid0);
+          array_copy(globs->DimGP, globs->varray.data(), vars[code].grid0);
         }
 
-      if (globs->Mean > 0 && (nmiss || vars[code].samp))
+      if (globs->Mean > 0 && (numMissVals || vars[code].samp))
         {
           if (vars[code].samp == nullptr)
             {
-              vars[code].samp = (int *) Malloc(dataSize * sizeof(int));
+              vars[code].samp = (int *) malloc(dataSize * sizeof(int));
               for (size_t i = 0; i < dataSize; ++i) vars[code].samp[i] = globs->MeanCount0;
             }
 
           for (size_t i = 0; i < gridSize; ++i)
-            if (is_not_equal(globs->Field[i], vars[code].missval)) vars[code].samp[i + dataOffset]++;
+            if (is_not_equal(globs->varray[i], vars[code].missval)) vars[code].samp[i + dataOffset]++;
         }
     }
 }
@@ -2332,7 +2323,8 @@ after_get_dataptr(struct Variable *vars, int code, int gridID, int zaxisID, int
 }
 
 void
-after_EchamAddRecord(const AfterControl *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID, size_t nmiss)
+after_EchamAddRecord(const AfterControl *globs, struct Variable *vars, int code, int gridID, int zaxisID, int levelID,
+                     size_t numMissVals)
 {
   auto gridtype = gridInqType(gridID);
   auto leveltype = zaxisInqType(zaxisID);
@@ -2341,7 +2333,7 @@ after_EchamAddRecord(const AfterControl *globs, struct Variable *vars, int code,
   auto dataSize = gridSize * nlevel;
   auto dataOffset = gridSize * levelID;
 
-  vars[code].nmiss0 += nmiss;
+  vars[code].numMissVals0 += numMissVals;
 
   if (gridtype == GRID_SPECTRAL)
     {
@@ -2364,11 +2356,11 @@ after_EchamAddRecord(const AfterControl *globs, struct Variable *vars, int code,
           vars[code].sfit = true;
         }
 
-      if (globs->Mean > 0 && (nmiss || vars[code].samp))
+      if (globs->Mean > 0 && (numMissVals || vars[code].samp))
         {
           if (vars[code].samp == nullptr)
             {
-              vars[code].samp = (int *) Malloc(dataSize * sizeof(int));
+              vars[code].samp = (int *) malloc(dataSize * sizeof(int));
               for (size_t i = 0; i < dataSize; ++i) vars[code].samp[i] = globs->MeanCount0;
             }
 
@@ -2412,7 +2404,7 @@ MakeDependencies(struct Variable *vars, int varcode, int depcode)
               vars[varcode].igridID = vars[depcode].igridID;
               vars[varcode].ogridID = vars[depcode].ogridID;
               vars[varcode].izaxisID = vars[depcode].izaxisID;
-              vars[varcode].ozaxisID = vars[depcode].ozaxisID;
+              if (varcode != GEOPOTHEIGHT) vars[varcode].ozaxisID = vars[depcode].ozaxisID;
             }
         }
     }
@@ -2479,10 +2471,6 @@ CheckDependencies(struct Variable *vars, int analysisdata)
   MakeDependencies(vars, SW_TOP_CLF, 187);
   MakeDependencies(vars, LW_TOP_CLF, 179);
   MakeDependencies(vars, LW_TOP_CLF, 188);
-  MakeDependencies(vars, NET_TOP_CLF, 178);
-  MakeDependencies(vars, NET_TOP_CLF, 179);
-  MakeDependencies(vars, NET_TOP_CLF, 187);
-  MakeDependencies(vars, NET_TOP_CLF, 188);
 
   MakeDependencies(vars, ALL_WATER, 222);
   MakeDependencies(vars, LOW_WATER, 222);
@@ -2580,7 +2568,7 @@ after_EchamDependencies(struct Variable *vars, int ncodes, int type, int source)
   MakeDependencies(vars, PRECIP, 142);
   MakeDependencies(vars, PRECIP, 143);
 
-  if (source != S_ECHAM5)
+  if (source != ECHAM5_Source)
     {
       MakeDependencies(vars, NET_WATER, 142);
       MakeDependencies(vars, NET_WATER, 143);
@@ -2599,7 +2587,7 @@ after_EchamDependencies(struct Variable *vars, int ncodes, int type, int source)
       MakeDependencies(vars, NET_HEAT, 177);
       MakeDependencies(vars, NET_HEAT, 218);
       /*
-        if ( source == S_ECHAM5 )
+        if ( source == ECHAM5_Source )
         {
         MakeDependencies(vars, NET_HEAT, 206);
         MakeDependencies(vars, NET_HEAT, 208);
@@ -2638,43 +2626,32 @@ after_legini_setup(AfterControl &globs, struct Variable *vars)
   long dimsp = (ntr + 1) * (ntr + 2);
   long pdim = (dimsp / 2) * nlat;
 
-  globs.poli = (double *) Malloc(pdim * sizeof(double));
+  globs.poli.resize(pdim);
 
   if (!globs.AnalysisData)
     {
-      if (globs.Type >= 20) globs.pold = (double *) Malloc(pdim * sizeof(double));
-      if (vars[DPSDY].needed) globs.pdev = (double *) Malloc(pdim * sizeof(double));
+      if (globs.Type >= 20) globs.pold.resize(pdim);
+      if (vars[DPSDY].needed) globs.pdev.resize(pdim);
     }
 
   if ((vars[DIVERGENCE].needed || vars[VORTICITY].needed || vars[VELOPOT].needed || vars[STREAM].needed) && globs.Type > 20)
     {
-      globs.pol2 = (double *) Malloc(pdim * sizeof(double));
-      globs.pol3 = (double *) Malloc(pdim * sizeof(double));
+      globs.pol2.resize(pdim);
+      globs.pol3.resize(pdim);
     }
 
   if (globs.AnalysisData && (globs.Type == 70) && globs.Gaussian && !globs.Spectral)
     {
-      if (globs.poli)
-        {
-          Free(globs.poli);
-          globs.poli = nullptr;
-        }
-      if (globs.pol2)
-        {
-          Free(globs.pol2);
-          globs.pol2 = nullptr;
-        }
-      if (globs.pol3)
-        {
-          Free(globs.pol3);
-          globs.pol3 = nullptr;
-        }
+      if (globs.poli.size()) varray_free(globs.poli);
+      if (globs.pol2.size()) varray_free(globs.pol2);
+      if (globs.pol3.size()) varray_free(globs.pol3);
       return;
     }
 
-  after_legini_full(ntr, nlat, globs.poli, globs.pold, globs.pdev, globs.pol2, globs.pol3, globs.coslat);
+  after_legini_full(ntr, nlat, globs.poli.data(), globs.pold.data(), globs.pdev.data(), globs.pol2.data(), globs.pol3.data(),
+                    globs.coslat.data());
 
   for (long jgl = 0; jgl < nlat; ++jgl) globs.rcoslat[jgl] = 1.0 / globs.coslat[jgl];
 
-  for (long jgl = 0; jgl < nlat; ++jgl) globs.DerivationFactor[jgl] = globs.rcoslat[jgl] / PlanetRadius;
+  for (long jgl = 0; jgl < nlat; ++jgl) globs.derivationFactor[jgl] = globs.rcoslat[jgl] / PlanetRadiusDefault;
 }
diff --git a/src/bitinformation.h b/src/bitinformation.h
index 6bd8d2e09a1150789ba4d1aaa26e6716a099b582..41d38fe164bd993744a541227788337bd6c28283 100644
--- a/src/bitinformation.h
+++ b/src/bitinformation.h
@@ -1,6 +1,8 @@
 #ifndef BITINFORMATION_H
 #define BITINFORMATION_H
 
+#include <cstddef>
+
 constexpr int NBITS = 32;  // Number of bits in type `float`
 
 struct MutualInformation
diff --git a/src/cdi_lockedIO.cc b/src/cdi_lockedIO.cc
index 823e3b310b535e23cef2dbb69b6a3ea8a3ac1128..862a1bca698db5024388cce93bc0b236dc86e0a5 100644
--- a/src/cdi_lockedIO.cc
+++ b/src/cdi_lockedIO.cc
@@ -14,14 +14,14 @@
 
 #include <mutex>
 
-static std::mutex streamOpenReadMutex;
+static std::mutex streamOpenMutex;
 static std::mutex streamMutex;
 
 int
 stream_open_read_locked(const char *const p_filename)
 {
   open_lock();
-  const auto streamID = streamOpenRead(p_filename);
+  auto streamID = streamOpenRead(p_filename);
   open_unlock();
   if (streamID < 0) cdi_open_error(streamID, "Open failed on >%s<", p_filename);
 
@@ -29,7 +29,7 @@ stream_open_read_locked(const char *const p_filename)
 }
 
 void
-stream_close_locked(const int p_fileID)
+stream_close_locked(int p_fileID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
   streamClose(p_fileID);
@@ -37,7 +37,7 @@ stream_close_locked(const int p_fileID)
 }
 
 void
-stream_inq_rec_locked(const int p_fileID, int *const p_varID, int *const p_levelID)
+stream_inq_rec_locked(int p_fileID, int *const p_varID, int *const p_levelID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
   streamInqRecord(p_fileID, p_varID, p_levelID);
@@ -45,7 +45,7 @@ stream_inq_rec_locked(const int p_fileID, int *const p_varID, int *const p_level
 }
 
 void
-stream_def_rec_locked(const int p_fileID, const int p_varID, const int p_levelID)
+stream_def_rec_locked(int p_fileID, int p_varID, int p_levelID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
   streamDefRecord(p_fileID, p_varID, p_levelID);
@@ -53,23 +53,23 @@ stream_def_rec_locked(const int p_fileID, const int p_varID, const int p_levelID
 }
 
 void
-stream_readrecord_float_locked(const int p_fileID, float *const p_data, size_t *const p_nmiss)
+stream_readrecord_float_locked(int p_fileID, float *const p_data, size_t *const p_numMissVals)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  streamReadRecordF(p_fileID, p_data, p_nmiss);
+  streamReadRecordF(p_fileID, p_data, p_numMissVals);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
 }
 
 void
-stream_readrecord_double_locked(const int p_fileID, double *const p_data, size_t *const p_nmiss)
+stream_readrecord_double_locked(int p_fileID, double *const p_data, size_t *const p_numMissVals)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  streamReadRecord(p_fileID, p_data, p_nmiss);
+  streamReadRecord(p_fileID, p_data, p_numMissVals);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
 }
 
 void
-stream_def_vlist_locked(const int p_fileID, const int p_vlistID)
+stream_def_vlist_locked(int p_fileID, int p_vlistID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
   streamDefVlist(p_fileID, p_vlistID);
@@ -77,52 +77,52 @@ stream_def_vlist_locked(const int p_fileID, const int p_vlistID)
 }
 
 int
-stream_inq_vlist_locked(const int p_fileID)
+stream_inq_vlist_locked(int p_fileID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  const auto vlistID = streamInqVlist(p_fileID);
+  auto vlistID = streamInqVlist(p_fileID);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
 
   return vlistID;
 }
 
 void
-stream_write_record_double_locked(const int p_fileID, const double *const p_data, const size_t p_nmiss)
+stream_write_record_double_locked(int p_fileID, const double *const p_data, const size_t p_numMissVals)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  streamWriteRecord(p_fileID, p_data, p_nmiss);
+  streamWriteRecord(p_fileID, p_data, p_numMissVals);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
 }
 
 void
-stream_write_record_float_locked(const int p_fileID, const float *const p_data, const size_t p_nmiss)
+stream_write_record_float_locked(int p_fileID, const float *const p_data, const size_t p_numMissVals)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  streamWriteRecordF(p_fileID, p_data, p_nmiss);
+  streamWriteRecordF(p_fileID, p_data, p_numMissVals);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
 }
 
 int
-stream_inq_time_step_locked(const int p_fileID, const int p_tsID)
+stream_inq_time_step_locked(int p_fileID, int p_tsID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  const auto nrecs = streamInqTimestep(p_fileID, p_tsID);
+  auto nrecs = streamInqTimestep(p_fileID, p_tsID);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
 
   return nrecs;
 }
 
 int
-stream_def_time_step_locked(const int p_fileID, const int p_tsID)
+stream_def_time_step_locked(int p_fileID, int p_tsID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
-  const auto success = streamDefTimestep(p_fileID, p_tsID);
+  auto success = streamDefTimestep(p_fileID, p_tsID);
   if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
   return success;
 }
 
 int
-stream_copy_record_locked(const int p_fileID, const int p_targetFileID)
+stream_copy_record_locked(int p_fileID, int p_targetFileID)
 {
   if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
   streamCopyRecord(p_fileID, p_targetFileID);
@@ -131,7 +131,7 @@ stream_copy_record_locked(const int p_fileID, const int p_targetFileID)
 }
 
 void
-vlist_copy_flag_locked(const int p_vlistID2, const int p_vlistID1)
+vlist_copy_flag_locked(int p_vlistID2, int p_vlistID1)
 {
   cthread_mutex_lock(streamMutex);
   vlistCopyFlag(p_vlistID2, p_vlistID1);
@@ -141,17 +141,17 @@ vlist_copy_flag_locked(const int p_vlistID2, const int p_vlistID1)
 void
 open_lock(void)
 {
-  cthread_mutex_lock(Threading::cdoLockIO ? streamMutex : streamOpenReadMutex);
+  cthread_mutex_lock(Threading::cdoLockIO ? streamMutex : streamOpenMutex);
 }
 
 void
 open_unlock(void)
 {
-  cthread_mutex_unlock(Threading::cdoLockIO ? streamMutex : streamOpenReadMutex);
+  cthread_mutex_unlock(Threading::cdoLockIO ? streamMutex : streamOpenMutex);
 }
 
 void
-cdo_vlist_copy_flag(const int vlistID2, const int vlistID1)
+cdo_vlist_copy_flag(int vlistID2, int vlistID1)
 {
   vlist_copy_flag_locked(vlistID2, vlistID1);
 }
diff --git a/src/cdi_lockedIO.h b/src/cdi_lockedIO.h
index 2ea22bb35fd863b692ad2ce475f802232d8a0102..47742ca329f250c061c7d8f23bec218675eb9bac 100644
--- a/src/cdi_lockedIO.h
+++ b/src/cdi_lockedIO.h
@@ -1,16 +1,18 @@
 #ifndef CDI_LOCKEDIO_H
 #define CDI_LOCKEDIO_H
 
+#include <cstddef>
+
 int stream_open_read_locked(const char *filename);
 void stream_close_locked(int p_fileID);
 void stream_inq_rec_locked(int p_fileID, int *p_varID, int *p_levelID);
 void stream_def_rec_locked(int p_fileID, int p_varID, int levelID);
-void stream_readrecord_float_locked(int p_fileID, float *p_data, size_t *p_nmiss);
-void stream_readrecord_double_locked(int p_fileID, double *p_data, size_t *p_nmiss);
+void stream_readrecord_float_locked(int p_fileID, float *p_data, size_t *p_numMissVals);
+void stream_readrecord_double_locked(int p_fileID, double *p_data, size_t *p_numMissVals);
 void stream_def_vlist_locked(int p_fileID, int p_vlistID);
 int stream_inq_vlist_locked(int p_fileID);
-void stream_write_record_double_locked(int p_fileID, const double *const p_data, size_t p_nmiss);
-void stream_write_record_float_locked(int p_fileID, const float *const p_data, size_t p_nmiss);
+void stream_write_record_double_locked(int p_fileID, const double *const p_data, size_t p_numMissVals);
+void stream_write_record_float_locked(int p_fileID, const float *const p_data, size_t p_numMissVals);
 int stream_inq_time_step_locked(int p_fileID, int p_tsID);
 int stream_def_time_step_locked(int p_fileID, int p_tsID);
 int stream_copy_record_locked(int p_fileID, int p_targetFileID);
diff --git a/src/cdo.cc b/src/cdo.cc
index 9e19c5defd270c324c099354f4a95de27aff4a1d..09cf0149452afd0f936947d159f62cb78a923946 100644
--- a/src/cdo.cc
+++ b/src/cdo.cc
@@ -29,8 +29,10 @@
 #endif
 #include <cfenv>
 #include <sys/stat.h>
+#include <sys/resource.h>  // getrlimit
 #include <unistd.h> /* sysconf */
 #include <cstring>
+#include <cstdlib>
 #include <csignal>
 
 #include <cdi.h>
@@ -43,12 +45,12 @@
 #include "param_conversion.h"
 #include "progress.h"
 
-#include "module_list.h"
 #include "module_info.h"
 #include "percentiles.h"
 #include "util_wildcards.h"
 #include "util_string.h"
 #include "process_int.h"
+#include "processManager.h"
 #include "cdo_options.h"
 #include "timer.h"
 #include "commandline.h"
@@ -57,14 +59,13 @@
 #include "cdo_features.h"
 #include "cdo_zaxis.h"
 #include "compare.h"
-#include "dmemory.h"
 #include "table.h"
 #include "datetime.h"
 #include "remap_grid_cell_search.h"
 #include "cdo_pthread.h"
 #include "institution.h"
-#include "cdo_apply.h"
 #include "parser.h"
+#include "factory.h"
 
 static ProcessManager g_processManager;
 
@@ -278,7 +279,7 @@ cdo_usage()
   */
   pad = CLIOptions::pad_size_terminal('=');
   fprintf(target, "%s\n", pad.c_str());
-  fprintf(target, "    CDO version %s, Copyright (C) 2002-2023 MPI für Meteorologie\n", VERSION);
+  fprintf(target, "    CDO version %s, Copyright (C) 2002-2024 MPI für Meteorologie\n", VERSION);
   fprintf(target, "    This is free software and comes with ABSOLUTELY NO WARRANTY\n");
   fprintf(target, "    Report bugs to <https://mpimet.mpg.de/cdo>\n\n");
   pad = CLIOptions::pad_size_terminal('=');
@@ -301,40 +302,6 @@ cdo_init_is_tty()
   if (S_ISCHR(statbuf.st_mode)) cdo::stderrIsTerminal = true;
 }
 
-static void
-cdo_print_help(const char **help)
-{
-  if (!help)
-    fprintf(stderr, "No help available for this operator!\n");
-  else
-    {
-      size_t help_size = 0;
-      while (help[help_size]) help_size++;
-      for (size_t i = 0; i < help_size; ++i)
-        {
-          auto doPrint = !(help[i][0] == '\0' && help[i + 1][0] == ' ');
-          if (doPrint)
-            {
-              if (color_enabled())
-                {
-                  if (cdo_cmpstr(help[i], "NAME") || cdo_cmpstr(help[i], "SYNOPSIS") || cdo_cmpstr(help[i], "DESCRIPTION")
-                      || cdo_cmpstr(help[i], "OPERATORS") || cdo_cmpstr(help[i], "NAMELIST") || cdo_cmpstr(help[i], "PARAMETER")
-                      || cdo_cmpstr(help[i], "ENVIRONMENT") || cdo_cmpstr(help[i], "NOTE") || cdo_cmpstr(help[i], "EXAMPLES"))
-                    {
-                      set_text_color(stdout, BRIGHT);
-                      fprintf(stdout, "%s", help[i]);
-                      reset_text_color(stdout);
-                      fprintf(stdout, "\n");
-                    }
-                  else
-                    fprintf(stdout, "%s\n", help[i]);
-                }
-              else { fprintf(stdout, "%s\n", help[i]); }
-            }
-        }
-    }
-}
-
 #undef IsBigendian
 #define IsBigendian() (u_byteorder.c[sizeof(long) - 1])
 
@@ -763,10 +730,10 @@ print_system_info()
   fprintf(stderr, "STD ANSI C          = %d\n", __STDC__);
 #endif
 #ifdef __STD_VERSION__
-  fprintf(stderr, "STD VERSION         = %ld\n", (long)__STD_VERSION__);
+  fprintf(stderr, "STD VERSION         = %ld\n", (long) __STD_VERSION__);
 #endif
 #ifdef __STDC_VERSION__
-  fprintf(stderr, "STDC VERSION        = %ld\n", (long)__STDC_VERSION__);
+  fprintf(stderr, "STDC VERSION        = %ld\n", (long) __STDC_VERSION__);
 #endif
 #ifdef __STD_HOSTED__
   fprintf(stderr, "STD HOSTED          = %d\n", __STD_HOSTED__);
@@ -818,6 +785,8 @@ cdo_set_options()
       fprintf(stderr, "\n");
     }
 
+  if (Options::test) cdiDefGlobal("THREADSAFE", 1);
+
   if (Options::CMOR_Mode) cdiDefGlobal("CMOR_MODE", Options::CMOR_Mode);  // TODO maybe reposition into effect of "cmor"
   if (Options::CDO_Reduce_Dim) cdiDefGlobal("REDUCE_DIM", Options::CDO_Reduce_Dim);
   if (CDO_netcdf_hdr_pad > 0) cdiDefGlobal("NETCDF_HDR_PAD", CDO_netcdf_hdr_pad);
@@ -848,7 +817,11 @@ evaluate_except_options(const std::string &arg)
   // clang-format on
   return except;
 }
-
+/*
+#if defined(__APPLE__) && defined(__MACH__)
+#include <libproc.h>
+#endif
+*/
 static void
 cdo_rusage(void)
 {
@@ -869,6 +842,20 @@ cdo_rusage(void)
       fprintf(stderr, "  Swaps:         %5ld\n", ru.ru_nswap);
       fprintf(stderr, "  Disk read:     %5ld block%s\n", ru.ru_inblock, ADD_PLURAL(ru.ru_inblock));
       fprintf(stderr, "  Disk Write:    %5ld block%s\n", ru.ru_oublock, ADD_PLURAL(ru.ru_oublock));
+      /*
+#if defined(__APPLE__) && defined(__MACH__)
+#ifdef RUSAGE_INFO_CURRENT
+      pid_t pid = fork();
+      rusage_info_current ruinfo;
+      int rusage_ret = proc_pid_rusage(pid, RUSAGE_INFO_CURRENT, (void **)&ruinfo);
+      if (rusage_ret >= 0 && ruinfo.ri_lifetime_max_phys_footprint > 0)
+        {
+          fprintf(stderr, "  Peak memory:   %.2f MBytes\n", ruinfo.ri_lifetime_max_phys_footprint / 1.0);
+          fprintf(stderr, "  Peak memory:   %.2f MBytes\n", ruinfo.ri_lifetime_max_phys_footprint / (1024.0 * 1024.0));
+        }
+#endif
+#endif
+      */
     }
 #endif
 }
@@ -930,7 +917,9 @@ static void
 print_operator_attributes(const std::string &argument)
 {
   ModListOptions local_modListOpt;
+
   local_modListOpt.parse_request(argument);
+
   operator_print_list(local_modListOpt);
 }
 
@@ -983,10 +972,10 @@ predefined_tables(int p_padding)
   constexpr int id_padding = 4;
   int padding = p_padding + id_padding;
   int numTables = tableInqNumber();
-  std::string tables = std::string("Predefined tables: ");
+  std::string tables{"Predefined tables: "};
   for (int id = 0; id < numTables; id++)
     {
-      if (id % 7 == 6) tables += "\n" + std::string(padding, ' ');
+      if (id % 7 == 6) tables += std::string("\n") + std::string(padding, ' ');
       if ((name = tableInqNamePtr(id))) tables += std::string(name);
       if (id < numTables - 1) tables += ",";
     }
@@ -1050,6 +1039,11 @@ setup_cli_options()
       })
       ->add_effect([&](const std::string &argument) {
         auto [success, tokens] = tokenize_comma_seperated_int_list(argument);
+        if (tokens.empty())
+          {
+            print_debug_options();
+            exit(EXIT_SUCCESS);
+          }
 
         unsigned cdoDebugLevel = 0;
         unsigned cdiDebugLevel = 0;
@@ -1210,7 +1204,7 @@ setup_cli_options()
       ->describe_argument("module name")
       ->set_category("Info")
       ->add_effect([&](const std::string &argument) {
-        auto names = get_module_operator_names(argument);
+        auto names = Factory::get_module_operator_names(argument);
         if (names.empty())
           {
             std::string errstr = "Module " + argument + " not found\n";
@@ -1252,7 +1246,7 @@ setup_cli_options()
 
   CLIOptions::option("help", "h")
       ->describe_argument("operator")
-      ->add_effect([&](const std::string &argument) { cdo_print_help(operator_help(argument)); })
+      ->add_effect([&](const std::string &operator_name) { cdo_print_help(Factory::get_help(operator_name)); })
       ->on_empty_argument([]() { cdo_usage(); })
       ->aborts_program(true)
       ->set_category("Help")
@@ -1308,7 +1302,7 @@ setup_cli_options()
 
   CLIOptions::option("institution", "i")
       ->describe_argument("institute_name")
-      ->add_effect([&](const std::string &argument) { define_institution(argument.c_str()); })
+      ->add_effect([&](const std::string &argument) { define_institution(argument); })
       ->add_help("Sets institution name.");
 
   CLIOptions::option("chunktype", "k")
@@ -1550,8 +1544,6 @@ main(int argc, char *argv[])
 
   cdo_init_is_tty();
 
-  memExitOnError();
-
   Options::CDO_Reduce_Dim = 0;
 
   // mallopt(M_MMAP_MAX, 0);
diff --git a/src/cdoStream.cc b/src/cdoStream.cc
index 99df8188ab970eab15b2c48ed02152333aa644e4..dcd2e113f8c6ede90bbc309f70ecbf2aae048083 100644
--- a/src/cdoStream.cc
+++ b/src/cdoStream.cc
@@ -5,13 +5,6 @@
 
 */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string>
-#include <cassert>
-
 #include "cdoStream.h"
 #include "cdo_options.h"
 
@@ -31,16 +24,3 @@ CdoStream::getTsID()
 {
   return m_tsID;
 }
-
-int
-CdoStream::getVarID()
-{
-  return m_varID;
-}
-
-int
-CdoStream::getVlistID()
-{
-  return m_vlistID;
-}
-
diff --git a/src/cdoStream.h b/src/cdoStream.h
index 756c8c82c63a9e2d76ee7e177369a91f279c2cb2..394a80f4412781eba72ef80403d68434cbcbcd8a 100644
--- a/src/cdoStream.h
+++ b/src/cdoStream.h
@@ -1,11 +1,6 @@
 #ifndef CDOSTREAM_H
 #define CDOSTREAM_H
 
-#ifdef HAVE_CONFIG_H
-#include "config.h" /* _FILE_OFFSET_BITS influence off_t */
-#endif
-
-#include <sys/types.h> /* off_t */
 #include <vector>
 #include <memory>
 
@@ -34,13 +29,13 @@ public:
   virtual void inq_record(int *varID, int *levelID) = 0;
   virtual void defRecord(int varID, int levelID) = 0;
 
-  virtual void read_record(float *const p_data, size_t *nmiss) = 0;
-  virtual void read_record(double *const p_data, size_t *nmiss) = 0;
-  virtual void read_record(Field *const p_field, size_t *nmiss) = 0;
+  virtual void read_record(float *const p_data, size_t *numMissVals) = 0;
+  virtual void read_record(double *const p_data, size_t *numMissVals) = 0;
+  virtual void read_record(Field *const p_field, size_t *numMissVals) = 0;
 
-  virtual void write_record(const float *const p_data, size_t nmiss) = 0;
-  virtual void write_record(const double *const p_data, size_t nmiss) = 0;
-  virtual void write_record(const Field *const p_field, size_t nmiss) = 0;
+  virtual void write_record(const float *const p_data, size_t numMissVals) = 0;
+  virtual void write_record(const double *const p_data, size_t numMissVals) = 0;
+  virtual void write_record(const Field *const p_field, size_t numMissVals) = 0;
 
   virtual void copyRecord(CdoStreamID dest) = 0;
 
@@ -56,8 +51,6 @@ public:
 
   int get_id();
   int getTsID();
-  int getVarID();
-  int getVlistID();
 
   int m_cdoStreamID = -1;  // aka the id of the pstream
   int m_filetype = CDI_UNDEFID;
diff --git a/src/cdo_apply.cc b/src/cdo_apply.cc
deleted file mode 100644
index 71913aa59702995806d1a7311e7e9422dbd2a408..0000000000000000000000000000000000000000
--- a/src/cdo_apply.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
-  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
-
-  Author: Uwe Schulzweida
-          Oliver Heidmann
-
-*/
-
-#include <vector>
-#include <iostream>
-#include "modules.h"
-#include "cdo_output.h"
-#include "cdo_apply.h"
-
-static ApplyStatus errState = ApplyStatus::OK;
-
-static std::string
-parse_arg(const std::string &oper)
-{
-  if (oper == "")
-    {
-      fprintf(stderr, "cdo apply: no operator given for apply.\n");
-      errState = ApplyStatus::MISSING_ARG;
-    }
-
-  const auto mod = get_module(oper);
-  if (mod.get_stream_in_cnt() != 1)
-    {
-      fprintf(stderr, "cdo apply: operator %s can not be used with apply.\n", oper.c_str());
-      if (mod.get_stream_in_cnt() == -1)
-        {
-          fprintf(stderr, "           %s has variable input.\n", oper.c_str());
-          errState = ApplyStatus::ARG_VARIABLE_INPUT;
-        }
-
-      if (mod.get_stream_in_cnt() == 0)
-        {
-          fprintf(stderr, "           %s has no input.\n", oper.c_str());
-          errState = ApplyStatus::ARG_NO_INPUT;
-        }
-    }
-  if (mod.get_stream_in_cnt() > 1)
-    {
-      fprintf(stderr, "           %s has more than one output.\n", oper.c_str());
-      errState = ApplyStatus::ARG_TOO_MANY_OUT;
-    }
-
-  return oper;
-}
-
-static std::vector<std::string>
-generate_tokens(const std::string &p_oper)
-{
-  std::vector<std::string> result;
-
-  auto end = p_oper.find(' ');
-  auto start = 0;
-  while (end != std::string::npos && errState == ApplyStatus::OK)
-    {
-      auto oper = p_oper.substr(start, end - start);
-      if (oper[0] == '-') oper = parse_arg(oper);
-      result.push_back(oper);
-      start = end + 1;
-      end = p_oper.find(' ', start);
-    }
-  auto oper = p_oper.substr(start, end - start);
-  if (oper[0] == '-') oper = parse_arg(oper);
-  result.push_back(oper);
-
-  return result;
-}
-
-static std::vector<std::string>::iterator
-expand(const std::vector<std::string> &p_oper, std::vector<std::string> &p_result, std::vector<std::string> &p_argv,
-       std::vector<std::string>::iterator p_start)
-{
-  auto argvIter = p_start;
-  while (argvIter != p_argv.end() && (*(argvIter))[0] != ']')
-    {
-      auto currentArg = (*(argvIter));
-      if (currentArg[0] == '[')
-        {
-          fprintf(stderr, "cdo apply: brackets not allowed for command apply.\n");
-          errState = ApplyStatus::BRACKET_USED;
-          return argvIter;
-        }
-      if (currentArg[0] == '-')
-        {
-          const auto mod = get_module(currentArg.c_str());
-          if (mod.get_stream_in_cnt() > 0)
-            {
-              fprintf(stderr, "cdo apply: operators with inputs are not allowed for command apply.\n");
-              errState = ApplyStatus::OPER_WITH_INPUT_USED;
-              return argvIter;
-            }
-        }
-      for (auto &oper : p_oper) { p_result.push_back(oper); }
-      p_result.push_back(*argvIter);
-      ++argvIter;
-    }
-
-  if (argvIter == p_argv.end() || (*(argvIter))[0] != ']')
-    {
-      fprintf(stderr, "cdo apply: missing closing bracket.\n");
-      errState = ApplyStatus::MISSING_CLOSING_BRACKET;
-    }
-
-  return argvIter;
-}
-
-static std::vector<std::string>
-scan(std::vector<std::string> p_argv)
-{
-  std::vector<std::string> newArgv = {};
-  if (p_argv[0].compare(0, strlen("-apply,"), "-apply,") == 0)
-    {
-      fprintf(stderr, "cdo apply: apply can not be first.\n");
-      errState = ApplyStatus::APPLY_USED_FIRST;
-      return p_argv;
-    }
-  for (auto argvIter = p_argv.begin(); argvIter < p_argv.end() && errState == ApplyStatus::OK; argvIter++)
-    {
-      std::string currentArgv = *argvIter;
-      if (currentArgv.compare(0, 6, "-apply") == 0)
-        {
-          ++argvIter;
-          if ((*(argvIter))[0] == '[')
-            {
-              const auto pos = currentArgv.find(',');
-              if (pos != std::string::npos)
-                {
-                  auto parameter = currentArgv.substr(pos + 1);
-                  if (parameter.empty())
-                    {
-                      fprintf(stderr, "cdo apply: missing argument for apply.\n");
-                      errState = ApplyStatus::MISSING_ARG;
-                      break;
-                    }
-                  else if (parameter[0] != '-')
-                    {
-                      fprintf(stderr, "cdo apply: missing pipe symbol in apply argument: %s\n", parameter.c_str());
-                      errState = ApplyStatus::MISSING_PIPE_SYM;
-                      break;
-                    }
-                  auto tokens = generate_tokens(parameter);
-                  if (errState != ApplyStatus::OK) break;
-                  argvIter = expand(tokens, newArgv, p_argv, ++argvIter);
-                  if (errState != ApplyStatus::OK) break;
-                }
-              else
-                {
-                  fprintf(stderr, "cdo apply: missing argument for apply.\n");
-                  errState = ApplyStatus::MISSING_ARG;
-                  break;
-                }
-            }
-          else
-            {
-              fprintf(stderr, "cdo apply: Missing bracket after apply, apply can only be used with [].\n");
-              errState = ApplyStatus::MISSING_BRACKET;
-            }
-        }
-      else { newArgv.push_back(*argvIter); }
-    }
-
-  return newArgv;
-}
-
-std::vector<std::string>
-expand_apply(const std::vector<std::string> &p_argv, ApplyStatus &expandSuccess)
-{
-  errState = ApplyStatus::OK;
-  auto result = scan(p_argv);
-  expandSuccess = errState;
-  return result;
-}
diff --git a/src/cdo_apply.h b/src/cdo_apply.h
deleted file mode 100644
index e0fe7640fccaef4e576790a2bbd7748cc5611a8f..0000000000000000000000000000000000000000
--- a/src/cdo_apply.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef CDO_APPLY_H
-#define CDO_APPLY_H
-
-#include <vector>
-
-/*
- * this feature of cdo allows the use of the -apply keyword.
- * When used all files that are inside the ][ brackets of apply are prepended with given operator.
- */
-
-/*
- * checks for:
- *  missing bracket at pos after -apply -> error
- *  checks for new bracket -> error
- *  checks for no closing bracket -> error
- *  checks for apply not beeing at the first position in the argument string -> error
- *  checks for missing argument for apply
- *  checks for operators that are used within the apply brackets -> error
- */
-
-/*
- * supports:
- * operators with number of inputs = 1 (no others)
- * operators with arguments: written as -apply,-OPERNAME,arguments
- */
-enum class ApplyStatus : int
-{
-  OK = 0,
-  ARG_TOO_MANY_OUT = 1,
-  ARG_NO_INPUT = 2,
-  ARG_VARIABLE_INPUT = 3,
-  BRACKET_USED = 4,
-  OPER_WITH_INPUT_USED = 5,
-  MISSING_CLOSING_BRACKET = 6,
-  APPLY_USED_FIRST = 7,
-  MISSING_ARG = 8,
-  MISSING_BRACKET = 9,
-  MISSING_PIPE_SYM = 10
-};
-
-std::vector<std::string> expand_apply(const std::vector<std::string> &p_argv, ApplyStatus &p_status);
-
-#endif
diff --git a/src/cdo_cdi_wrapper.cc b/src/cdo_cdi_wrapper.cc
index f0c6de33e9c6b6e535a3a95928cf9262c35c347f..697f22fd472e1355eb14d241d33f4aee7bcf9bde 100644
--- a/src/cdo_cdi_wrapper.cc
+++ b/src/cdo_cdi_wrapper.cc
@@ -16,7 +16,7 @@ filetype_to_cstr(int filetype)
 {
   switch (filetype)
     {
-    // clang-format off
+      // clang-format off
     case CDI_FILETYPE_GRB:    return "GRIB";
     case CDI_FILETYPE_GRB2:   return "GRIB2";
     case CDI_FILETYPE_NC:     return "NetCDF";
@@ -29,7 +29,7 @@ filetype_to_cstr(int filetype)
     case CDI_FILETYPE_EXT:    return "EXTRA";
     case CDI_FILETYPE_IEG:    return "IEG";
     default:                  return "";
-    // clang-format on
+      // clang-format on
     }
 }
 
@@ -101,8 +101,6 @@ get_steptype_name(int tsteptype)
   return "unknown";
 }
 
-
-
 }  // namespace cdo
 
 int
@@ -215,4 +213,4 @@ get_healpix_params(int gridID)
   return std::make_pair(nside, hpOrder);
 }
 
-}
+}  // namespace cdo
diff --git a/src/cdo_cdi_wrapper.h b/src/cdo_cdi_wrapper.h
index d0d8712dd08658339aeeb91f8f78bad2b013d1b9..5bd90c7b364c89933ba3df27c997978feaf087cf 100644
--- a/src/cdo_cdi_wrapper.h
+++ b/src/cdo_cdi_wrapper.h
@@ -19,8 +19,7 @@ int str_to_datatype(const std::string &datatypeStr);
 
 const char *get_steptype_name(int tsteptype);
 
-}
-
+}  // namespace cdo
 
 // create Taxis(cdi) with added check/setting of taxisType
 int cdo_taxis_create(int taxisType);
@@ -45,6 +44,6 @@ std::string inq_var_units(int vlistID, int varID);
 
 std::pair<int, HpOrder> get_healpix_params(int gridID);
 
-}
+}  // namespace cdo
 
 #endif
diff --git a/src/cdo_exception.h b/src/cdo_exception.h
index 6a97164f449a10f76d1487869a3d17f0d5c5923f..6b858720c03c568353a68190a93db410409174cd 100644
--- a/src/cdo_exception.h
+++ b/src/cdo_exception.h
@@ -6,7 +6,10 @@
 
 struct CdoException : std::logic_error
 {
-  CdoException(const std::string &p_msg, const std::string &p_file, const std::string &p_line) : std::logic_error(p_msg), file(p_file), line(p_line) {}
+  CdoException(const std::string &p_msg, const std::string &p_file, const std::string &p_line)
+      : std::logic_error(p_msg), file(p_file), line(p_line)
+  {
+  }
   std::string file;
   std::string line;
 };
diff --git a/src/cdo_fctrans.cc b/src/cdo_fctrans.cc
index c5db60a8c91b31665ea0aa94746332355fe2290c..fee1fd52bc8450b26b985c59f2320cbed78f6869 100644
--- a/src/cdo_fctrans.cc
+++ b/src/cdo_fctrans.cc
@@ -76,6 +76,7 @@ fc2gp(const double *fc, double *gp, long nlat, long nlon, long nlev, long nfc)
     {
       fftw_free(ompmem[i].in_fft);
       fftw_free(ompmem[i].out_fft);
+      std::scoped_lock lock(fftwMutex);
       fftw_destroy_plan(ompmem[i].plan);
     }
 #else
@@ -137,6 +138,7 @@ gp2fc(const double *gp, double *fc, long nlat, long nlon, long nlev, long nfc)
     {
       fftw_free(ompmem[i].in_fft);
       fftw_free(ompmem[i].out_fft);
+      std::scoped_lock lock(fftwMutex);
       fftw_destroy_plan(ompmem[i].plan);
     }
 #else
diff --git a/src/cdo_fftw3.cc b/src/cdo_fftw3.cc
index 8b2c22d89701320259b8a38d0614ea527cc5d8f7..3cedd09dba0362692171661fe5c3dee714374837 100644
--- a/src/cdo_fftw3.cc
+++ b/src/cdo_fftw3.cc
@@ -59,6 +59,7 @@ fourier2grid(int gridID1, const Varray<double> &array1, Varray<double> &array2)
     {
       fftw_free(ompmem[i].in_fft);
       fftw_free(ompmem[i].out_fft);
+      std::scoped_lock lock(fftwMutex);
       fftw_destroy_plan(ompmem[i].plan);
     }
 }
@@ -114,9 +115,11 @@ grid2fourier(int gridID1, const Varray<double> &array1, int gridID2, Varray<doub
     {
       fftw_free(ompmem[i].in_fft);
       fftw_free(ompmem[i].out_fft);
+      std::scoped_lock lock(fftwMutex);
       fftw_destroy_plan(ompmem[i].plan);
     }
 }
+
 void
 filter_fftw(int nts, const std::vector<int> &fmasc, fftw_complex *fft_out, fftw_plan *p_T2S, fftw_plan *p_S2T)
 {
@@ -133,7 +136,9 @@ filter_fftw(int nts, const std::vector<int> &fmasc, fftw_complex *fft_out, fftw_
 
   return;
 }
+
 #else
+
 #include "cdo_output.h"
 void
 fourier2grid(int gridID1, const Varray<double> &array1, Varray<double> &array2)
diff --git a/src/cdo_fftw3.h b/src/cdo_fftw3.h
index b5248526d53add50ab678f1e17e7c23f8dca2f38..4e7a94fc5171d1a42013b19d35ac661c570bcfbd 100644
--- a/src/cdo_fftw3.h
+++ b/src/cdo_fftw3.h
@@ -7,17 +7,13 @@
 
 #include "varray.h"
 
+void fourier2grid(int gridID1, const Varray<double> &array1, Varray<double> &array2);
 
-void
-fourier2grid(int gridID1, const Varray<double> &array1, Varray<double> &array2);
-
-void
-grid2fourier(int gridID1, const Varray<double> &array1, int gridID2, Varray<double> &array2);
+void grid2fourier(int gridID1, const Varray<double> &array1, int gridID2, Varray<double> &array2);
 
 #ifdef HAVE_LIBFFTW3
 #include <fftw3.h>
-void
-filter_fftw(int nts, const std::vector<int> &fmasc, fftw_complex *fft_out, fftw_plan *p_T2S, fftw_plan *p_S2T);
+void filter_fftw(int nts, const std::vector<int> &fmasc, fftw_complex *fft_out, fftw_plan *p_T2S, fftw_plan *p_S2T);
 #endif
 
 #endif
diff --git a/src/cdo_getopt.cc b/src/cdo_getopt.cc
index 158fc9886634d2e79a8f967d64088753ad9e124d..83f3ad3016d0439bea19429a2f95e080a5b952a3 100644
--- a/src/cdo_getopt.cc
+++ b/src/cdo_getopt.cc
@@ -288,7 +288,7 @@ CLIOptions::option_from_envvar(const std::string &p_envvarName)
   std::string ENVVAR_SUFFIX = "CDO_";
   std::string optionName = p_envvarName.substr(ENVVAR_SUFFIX.size(), p_envvarName.size());
 
-  std::transform(optionName.begin(), optionName.end(), optionName.begin(), [](unsigned char c) { return std::tolower(c); });
+  std::ranges::transform(optionName, optionName.begin(), ::tolower);
 
   optionName = "--" + optionName;
 
diff --git a/src/cdo_getopt.h b/src/cdo_getopt.h
index 1f85b7b25735b63954dd2498786dc064d865dd1a..e5e26d5c4d89b84499c354cc183b8e7b5cfaac35 100644
--- a/src/cdo_getopt.h
+++ b/src/cdo_getopt.h
@@ -61,11 +61,10 @@ struct cdo_option_2
   cdo_option_2 *
   on_empty_argument(const std::function<void()> p_on_empty_argument)
   {
-    argument.on_empty_argument = [p_on_empty_argument](std::string){p_on_empty_argument();};
+    argument.on_empty_argument = [p_on_empty_argument](std::string) { p_on_empty_argument(); };
     return this;
   }
 
-
   cdo_option_2 *
   add_effect(const std::function<void(std::string argument)> p_effect)
   {
@@ -95,7 +94,6 @@ struct cdo_option_2
     return this;
   }
 
-
   cdo_option_2 *
   describe_argument(const std::string &desc)
   {
diff --git a/src/cdo_module.cc b/src/cdo_module.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dc408d9638fdd6b8a9fab5f3cf9d3cf1d16332ad
--- /dev/null
+++ b/src/cdo_module.cc
@@ -0,0 +1,107 @@
+#include "cdo_module.h"
+
+oper_t::oper_t() : help(default_help) {}
+
+oper_t::oper_t(const char *_name, int _f1, int _f2, const char *_enter)
+    : name(_name), f1(_f1), f2(_f2), enter(_enter), help(default_help)
+{
+}
+
+oper_t::oper_t(const char *_name, int _f1, int _f2) : name(_name), f1(_f1), f2(_f2), enter(nullptr), help(default_help) {}
+
+oper_t::oper_t(const char *_name, int _f1, int _f2, const char *_enter, const CdoHelp &p_help)
+    : name(_name), f1(_f1), f2(_f2), enter(_enter), help(p_help)
+{
+}
+oper_t::oper_t(const char *_name, int _f1, int _f2, const CdoHelp &p_help)
+    : name(_name), f1(_f1), f2(_f2), enter(nullptr), help(p_help)
+{
+}
+oper_t::oper_t(const char *_name) : name(_name), help(default_help) {}
+
+oper_t::oper_t(const char *_name, const CdoHelp &p_help) : name(_name), enter(nullptr), help(p_help) {}
+
+int
+CdoModule::get_id(const std::string &oper) const
+{
+  for (size_t i = 0; i < operators.size(); i++)
+    {
+      if (oper == operators[i].name) { return i; }
+    }
+  return -1;
+}
+
+std::string
+CdoModule::toString() const
+{
+  std::string inp = (constraints.streamInCnt >= 0) ? std::to_string(get_stream_in_cnt()) : "Arbitrary";
+  std::string out = (constraints.streamOutCnt >= 0) ? std::to_string(get_stream_out_cnt()) : "Output base";
+  std::string restriction = "none";
+
+  if (get_pos_restriction() == OnlyFirst) restriction = "Can only be the first operator";
+  if (get_pos_restriction() == FilesOnly)
+    (restriction != Green("none")) ? restriction = Yellow("Can only use files as input.")
+                                   : restriction += Yellow(", Can only use files as input");
+
+  std::string desc = "Input: " + inp + ", Ouput: " + out + ", Restricton: " + restriction;
+
+  return desc;
+}
+
+int
+CdoModule::get_stream_in_cnt() const
+{
+  return constraints.streamInCnt;
+}
+
+int
+CdoModule::get_stream_out_cnt() const
+{
+  return constraints.streamOutCnt;
+}
+
+int
+CdoModule::get_number() const
+{
+  return number;
+}
+
+int
+CdoModule::get_mode() const
+{
+  return mode;
+}
+
+int
+CdoModule::get_pos_restriction() const
+{
+  return constraints.pos_restriction;
+}
+
+int
+CdoModule::is_alias(const std::string &subject) const
+{
+  for (size_t i = 0; i < aliases.size(); i++)
+    {
+      if (aliases[i].alias == subject) { return i; }
+    }
+  return -1;
+}
+
+std::vector<oper_t>::const_iterator
+CdoModule::find_operator(const std::string &p_operatorName) const
+{
+
+  auto res
+      = std::find_if(begin(operators), end(operators), [&p_operatorName](const oper_t &o) { return o.name == p_operatorName; });
+  return res;
+}
+
+const CdoHelp &
+CdoModule::get_help(const std::string &p_operatorName) const
+{
+  auto oper_iter = find_operator(p_operatorName);
+  if (oper_iter == operators.end()) cdo_abort("Help for %s in module %s not found", p_operatorName, name);
+
+  return oper_iter->help;
+}
diff --git a/src/cdo_module.h b/src/cdo_module.h
new file mode 100644
index 0000000000000000000000000000000000000000..4fce514a429d8feb42dd98cab2ebc2b5524249f7
--- /dev/null
+++ b/src/cdo_module.h
@@ -0,0 +1,95 @@
+/*
+  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
+
+  Author: Uwe Schulzweida
+          Oliver Heidmann
+
+*/
+#ifndef CDO_MODULE_H
+#define CDO_MODULE_H
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "cdo_output.h"
+#include "operator_help.h"
+
+// Obase uses input name as base name for files e.g 'test' gets used as test_001 test_002 which are created inside the operator
+#define OBASE -1
+#define INTERNAL 0
+#define EXPOSED 1
+
+struct Alias
+{
+  Alias(const std::string &_alias, const std::string &_original) : alias(_alias), original(_original) {}
+  std::string alias;
+  std::string original;
+};
+
+enum PositionRestrictions
+{
+  NoRestriction = 0,
+  FilesOnly = 1,
+  OnlyFirst = 2
+};
+
+struct module_constraints
+{
+  short streamInCnt;   // Number of input streams
+  short streamOutCnt;  // Number of output streams
+  PositionRestrictions pos_restriction = NoRestriction;
+};
+
+class oper_t
+{
+  inline static const CdoHelp default_help = {};
+
+public:
+  std::string name;
+  int f1 = 0;
+  int f2 = 0;
+  const char *enter = nullptr;
+  const CdoHelp &help = default_help;
+  oper_t();
+  oper_t(const char *_name);
+  oper_t(const char *_name, int _f1, int _f2, const char *_enter);
+  oper_t(const char *_name, int _f1, int _f2);
+  oper_t(const char *_name, int _f1, int _f2, const CdoHelp &p_help);
+  oper_t(const char *_name, int _f1, int _f2, const char *_enter, const CdoHelp &p_help);
+  oper_t(const char *_name, const CdoHelp &p_help);
+  oper_t(const char *_name, int _f1, int _f2, const CdoHelp &&p_help) = delete;
+  oper_t(const char *_name, int _f1, int _f2, const char *_enter, const CdoHelp &&p_help) = delete;
+  oper_t(const char *_name, const CdoHelp &&p_help) = delete;
+
+  oper_t(int _f1, int _f2, const char *_name, const char *_enter);
+};
+
+struct CdoModule
+{
+
+public:
+  std::string name;
+  std::vector<oper_t> operators;  // Operator names
+  std::vector<Alias> aliases;
+  short mode;    // Module mode: 0:intern 1:extern
+  short number;  // Allowed number type
+  module_constraints constraints;
+
+  std::string toString() const;
+
+  int get_id(const std::string &oper) const;
+  int get_stream_in_cnt() const;
+  int get_stream_out_cnt() const;
+  int get_number() const;
+  int get_mode() const;
+  int get_pos_restriction() const;
+
+  // returns the alias id or if not found1
+  int is_alias(const std::string &subject) const;
+
+  std::vector<oper_t>::const_iterator find_operator(const std::string &p_operatorName) const;
+  const CdoHelp &get_help(const std::string &p_operatorName) const;
+};
+
+#endif
diff --git a/src/cdo_node_attach_exception.h b/src/cdo_node_attach_exception.h
index 7364f33647050605a8c4f5542c2bb0c860468fad..9d22c235020299b7c7fdf3d2f56af4c2ce3f9543 100644
--- a/src/cdo_node_attach_exception.h
+++ b/src/cdo_node_attach_exception.h
@@ -4,6 +4,7 @@
 #include <vector>
 #include <string>
 #include <memory>
+#include "node.h"
 #include "cdo_exception.h"
 
 struct NodeAttachException : public CdoException
diff --git a/src/cdo_options.cc b/src/cdo_options.cc
index ea4f46d4a30e9503090c8463b40f743fbcb8c664..465f1f9768bdc41676c9a0fd984da905defb76ff 100644
--- a/src/cdo_options.cc
+++ b/src/cdo_options.cc
@@ -146,11 +146,11 @@ set_compression(int streamID, int filetype)
 
   if (Options::filterId != 0)
     {
-      streamDefFilter(streamID, Options::filterId, (int)Options::filterParams.size(), Options::filterParams.data());
+      streamDefFilter(streamID, Options::filterId, (int) Options::filterParams.size(), Options::filterParams.data());
     }
 }
 
-static double pointSearchRadius = 180.0; // default point search radius in degrees
+static double pointSearchRadius = 180.0;  // default point search radius in degrees
 
 // set point search radius in degrees
 void
diff --git a/src/cdo_output.cc b/src/cdo_output.cc
index 72c54dc4d3f7751edb69cf13ac15f42441345b55..da3b94c7bf1908cf45e4f8633cfb86dba1d5c08c 100644
--- a/src/cdo_output.cc
+++ b/src/cdo_output.cc
@@ -32,6 +32,10 @@ unsigned PROCESS_MANAGER = 0;
 unsigned CDO_NODE = 0;
 unsigned PARSER = 0;
 unsigned PROCESS_INT = 0;
+unsigned FACTORY = 0;
+unsigned KVLIST = 0;
+unsigned MODULE_INFO = 0;
+
 
 std::string debug_option_string = "DebugLevels:\n"
                                   "     0: off \n"
@@ -51,7 +55,10 @@ std::string debug_option_string = "DebugLevels:\n"
                                   "    12: Process manager\n"
                                   "    13: CDO nodes\n"
                                   "    14: Parser\n"
-                                  "    15: Process interface\n";
+                                  "    15: Process interface\n"
+                                  "    16: Factory\n"
+                                  "    17: KVList\n"
+                                  "    16: Module Info\n";
 
 void
 print_debug_options()
@@ -65,7 +72,7 @@ void
 parse_debug_arguments(const std::vector<std::string> &tokens, unsigned &cdoDebugLevel, unsigned &cdiDebugLevel)
 {
 
-  for (std::string t : tokens)
+  for (const auto &t : tokens)
     {
       if (t.substr(0, 4).compare("ext=") == 0)
         {
@@ -88,10 +95,7 @@ parse_debug_arguments(const std::vector<std::string> &tokens, unsigned &cdoDebug
 
         default:
           if (int_token > 6) { cdoDebugLevel = (cdoDebugLevel | (1 << (int_token))); }
-          else
-            {
-              cdiDebugLevel = (cdiDebugLevel | (1 << (int_token - 1)));
-            }
+          else { cdiDebugLevel = (cdiDebugLevel | (1 << (int_token - 1))); }
         }
     }
 }
@@ -110,7 +114,10 @@ print_debug_levels(const unsigned cdoDebugLevel, const unsigned cdiDebugLevel)
               + "PROCESS_MANAGER: "  + std::string(PROCESS_MANAGER ? "ON" : "OFF") + "\n"
               + "CDO_NODE:        "  + std::string(CDO_NODE ? "ON" : "OFF")        + "\n"
               + "PARSER:          "  + std::string(PARSER ? "ON" : "OFF")          + "\n"
-              + "PROCESS_INT:     "  + std::string(PROCESS_INT ? "ON" : "OFF")     + "\n";
+              + "PROCESS_INT:     "  + std::string(PROCESS_INT ? "ON" : "OFF")     + "\n"
+              + "FACTORY:         "  + std::string(FACTORY ? "ON" : "OFF")         + "\n"
+              + "KVLIST:          "  + std::string(KVLIST ? "ON" : "OFF")          + "\n"
+              + "MODULE_INFO:          "  + std::string(MODULE_INFO ? "ON" : "OFF")          + "\n";
   std::cout << deb_level << std::endl;
 
   if (cdoDebugLevel  == 1)
@@ -141,6 +148,9 @@ set_debug(unsigned p_debug_level)
   if (p_debug_level & (1u << 13)) CDO_NODE = 1;
   if (p_debug_level & (1u << 14)) PARSER = 1;
   if (p_debug_level & (1u << 15)) PROCESS_INT = 1;
+  if (p_debug_level & (1u << 16)) FACTORY = 1;
+  if (p_debug_level & (1u << 17)) KVLIST = 1;
+  if (p_debug_level & (1u << 18)) MODULE_INFO = 1;
   MpMO::DebugLevel = p_debug_level;
   cdoDebug = (p_debug_level > 0);
 }
@@ -183,7 +193,7 @@ getGRB2ErrStr(const char *progname)
   const char *format = "To create a %s application with GRIB2 support use: ./configure --with-eccodes=<ECCODES root directory> ...";
   const size_t finalSize = std::snprintf(nullptr, 0, format, progname);
   char *errStr = (char *) malloc(finalSize + 1);
-  sprintf(errStr, format, progname);
+  std::snprintf(errStr, finalSize + 1, format, progname);
 
   return errStr;
 }
@@ -198,13 +208,13 @@ getNCErrString(int filetype, const char *progname)
   const char *format = "%s was build with a NetCDF version which doesn't support NetCDF%s data!";
   const size_t finalSize = std::snprintf(nullptr, 0, format, progname, ncv);
   char *errStr = (char *) malloc(finalSize + 1);
-  sprintf(errStr, format, progname, ncv);
+  std::snprintf(errStr, finalSize + 1, format, progname, ncv);
 #else
   const char *format
       = "To create a %s application with NetCDF%s support use: ./configure --with-netcdf=<NetCDF%s root directory> ...";
   const size_t finalSize = std::snprintf(nullptr, 0, format, progname, ncv, ncv);
   char *errStr = (char *) malloc(finalSize + 1);
-  sprintf(errStr, format, progname, ncv, ncv);
+  std::snprintf(errStr, finalSize + 1, format, progname, ncv, ncv);
 #endif
 
   return errStr;
diff --git a/src/cdo_output.h b/src/cdo_output.h
index 440cd87dda5d2b23ef4bf123db266af4bcda0472..761848ed204449344ebabe8cae070664502c4a88 100644
--- a/src/cdo_output.h
+++ b/src/cdo_output.h
@@ -19,6 +19,9 @@ extern unsigned CDO_NODE;
 extern unsigned PARSER;
 extern unsigned PROCESS_INT;
 extern unsigned CDO_DEBUG;
+extern unsigned FACTORY;
+extern unsigned KVLIST;
+extern unsigned MODULE_INFO;
 
 extern std::string debug_option_string;
 
diff --git a/src/cdo_query.cc b/src/cdo_query.cc
index 5bc424f18ac066432ad2938971c69b843d5dc55f..1986e90320bc3820f2ed9a6999a64f2afff40e21 100644
--- a/src/cdo_query.cc
+++ b/src/cdo_query.cc
@@ -104,7 +104,7 @@ set_query_parameter(const std::string &params, CdiQuery *query)
 
   KVList kvlist;
   kvlist.name = "QUERY";
-  if (kvlist.parse_arguments(paramsArgv.size(), paramsArgv) != 0) cdo_abort("Parse error!");
+  if (kvlist.parse_arguments(paramsArgv) != 0) cdo_abort("Parse error!");
   if (Options::cdoVerbose) kvlist.print();
 
   return set_query_parameter(kvlist, query);
diff --git a/src/cdo_read.cc b/src/cdo_read.cc
index 9466d922642ec2a7fe5f0cbd0721925c931b29df..8c57716fb32b362cbd64b1dd1818787c436ef224 100644
--- a/src/cdo_read.cc
+++ b/src/cdo_read.cc
@@ -53,12 +53,12 @@ cdo_read_timestepmask(const char *maskfile, std::vector<bool> &imask)
       if (nrecs != 1) cdo_abort("Internal error; unexprected number of records!");
 
       int varID, levelID;
-      size_t nmiss;
+      size_t numMissVals;
       double value;
       streamInqRecord(streamID, &varID, &levelID);
-      streamReadRecord(streamID, &value, &nmiss);
+      streamReadRecord(streamID, &value, &numMissVals);
 
-      imask[tsID] = !(nmiss || IS_EQUAL(value, 0));
+      imask[tsID] = !(numMissVals || IS_EQUAL(value, 0));
 
       tsID++;
     }
@@ -87,9 +87,9 @@ read_one_field(const char *text, const char *filename, Varray<double> &array)
   array.resize(gridsize);
 
   int varID, levelID;
-  size_t nmiss;
+  size_t numMissVals;
   streamInqRecord(streamID, &varID, &levelID);
-  streamReadRecord(streamID, array.data(), &nmiss);
+  streamReadRecord(streamID, array.data(), &numMissVals);
   streamClose(streamID);
 }
 
diff --git a/src/cdo_varlist.cc b/src/cdo_varlist.cc
index b8d6a628a66905d7b4cb070ffb4fcd42d93575f1..e59854ba4319cb8f7585168f95aa0d2cb8e430fb 100644
--- a/src/cdo_varlist.cc
+++ b/src/cdo_varlist.cc
@@ -58,7 +58,7 @@ varList_init(VarList &varList, int vlistID)
           auto haveAddoffset = (cdiInqKeyFloat(vlistID, varID, CDI_KEY_ADDOFFSET, &addoffset) == CDI_NOERR);
           auto haveScalefactor = (cdiInqKeyFloat(vlistID, varID, CDI_KEY_SCALEFACTOR, &scalefactor) == CDI_NOERR);
           auto isPacked = (haveAddoffset || haveScalefactor);
-          auto useFloatType = isFloatType(var.datatype) || (isIntType(var.datatype) && !isPacked);
+          auto useFloatType = (var.datatype == CDI_UNDEFID) || isFloatType(var.datatype) || (isIntType(var.datatype) && !isPacked);
           var.memType = useFloatType ? MemType::Float : MemType::Double;
         }
       else { var.memType = Options::CDO_Memtype; }
@@ -198,6 +198,7 @@ search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
       else if (code == gribcodes.lsp     && nlevels == 1)             varIDs.lnpsID = varID;
       else if (code == 777               && nlevels == 1)             varIDs.lnpsID2 = varID;
       else if (code == gribcodes.gheight && nlevels == numFullLevels) varIDs.gheightID = varID;
+      else if (code == gribcodes.gheight && nlevels == numFullLevels + 1) varIDs.gheightID = varID;
       else if (code == gribcodes.hum     && nlevels == numFullLevels) varIDs.humID = varID;
       // else if (code == 246 && nlevels == numFullLevels) varIDs.clwcID = varID;
       // else if (code == 247 && nlevels == numFullLevels) varIDs.ciwcID = varID;
@@ -224,10 +225,7 @@ varList_map(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag,
               if (varList1[varID1].name == varList2[varID2].name) break;
             }
           if (varID1 == nvars1) { cdo_abort("Variable %s not found in first input stream!", varList2[varID2].name); }
-          else
-            {
-              mapOfVarIDs[varID1] = varID2;
-            }
+          else { mapOfVarIDs[varID1] = varID2; }
         }
     }
   else
@@ -244,10 +242,7 @@ varList_map(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag,
               if (mapFlag == 3) continue;
               cdo_abort("Variable %s not found in second input stream!", varList1[varID1].name);
             }
-          else
-            {
-              mapOfVarIDs[varID1] = varID2;
-            }
+          else { mapOfVarIDs[varID1] = varID2; }
         }
     }
 
@@ -280,8 +275,7 @@ varList_map(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag,
 
       if (flag & static_cast<int>(CmpVlist::GridSize))
         {
-          if (varList1[varID1].gridsize != varList2[varID2].gridsize)
-            cdo_abort("Grid size of the input fields do not match!");
+          if (varList1[varID1].gridsize != varList2[varID2].gridsize) cdo_abort("Grid size of the input fields do not match!");
         }
 
       if (flag & static_cast<int>(CmpVlist::NumLevels))
@@ -324,8 +318,8 @@ varList_check_names(const VarList &varList1, const VarList &varList2)
   for (int varID = 0; varID < numVars; ++varID) names1[varID] = varList1[varID].name;
   for (int varID = 0; varID < numVars; ++varID) names2[varID] = varList2[varID].name;
 
-  std::sort(names1.begin(), names1.end());
-  std::sort(names2.begin(), names2.end());
+  ranges::sort(names1);
+  ranges::sort(names2);
 
   int varID;
   for (varID = 0; varID < numVars; ++varID)
@@ -382,7 +376,7 @@ varList_compare(const VarList &varList1, const VarList &varList2, CmpVlist cmpFl
 
   int numVars = varList1.size();
 
-  if (numVars != (int)varList2.size())
+  if (numVars != (int) varList2.size())
     {
       varList_print_missing_vars(varList1, varList2);
       cdo_abort("Input streams have different number of variables per timestep!");
diff --git a/src/cdo_varlist.h b/src/cdo_varlist.h
index 41ac122768e176eee3fc8b589071beb0c1085d37..a345de25bb7626a3c84263ad8d9f36e9d3da583f 100644
--- a/src/cdo_varlist.h
+++ b/src/cdo_varlist.h
@@ -20,7 +20,11 @@ enum class CmpVlist
   All = Name | Dim
 };
 
-inline CmpVlist operator | (CmpVlist lhs, CmpVlist rhs) { return (CmpVlist) (static_cast<int>(lhs) | static_cast<int>(rhs)); }
+inline CmpVlist
+operator|(CmpVlist lhs, CmpVlist rhs)
+{
+  return (CmpVlist) (static_cast<int>(lhs) | static_cast<int>(rhs));
+}
 
 struct CdoVar
 {
diff --git a/src/cdo_vlist.cc b/src/cdo_vlist.cc
index 5d23cd3bd015bd90e46b4be3849851286b513048..87666c43d28fe59d62935595eddd464946d915ec 100644
--- a/src/cdo_vlist.cc
+++ b/src/cdo_vlist.cc
@@ -167,10 +167,7 @@ cdo_compare_grids(int gridID1, int gridID2)
           else
             cdo_warning("xsize of input grids differ!");
         }
-      else if (gridType1 == GRID_CURVILINEAR || gridType2 == GRID_UNSTRUCTURED)
-        {
-          compare_grid_unstructured(gridID1, gridID2);
-        }
+      else if (gridType1 == GRID_CURVILINEAR || gridType2 == GRID_UNSTRUCTURED) { compare_grid_unstructured(gridID1, gridID2); }
     }
   else if (gridInqSize(gridID1) > 1)
     {
diff --git a/src/cdo_zaxis.cc b/src/cdo_zaxis.cc
index fd9c3a7f2d72a6ce41e476417a37a7dcbb0e34d4..7a71ee4cdb659b983f6b231ba989b3b5e25c4248 100644
--- a/src/cdo_zaxis.cc
+++ b/src/cdo_zaxis.cc
@@ -277,7 +277,7 @@ zaxis_from_file(FILE *zfp, const char *filename)
 }
 
 static void
-gen_zaxis_height(ZaxisDesciption& zaxis, const std::string& zaxisname)
+gen_zaxis_height(ZaxisDesciption &zaxis, const std::string &zaxisname)
 {
   int zaxistype = ZAXIS_HEIGHT;
 
@@ -308,7 +308,7 @@ gen_zaxis_height(ZaxisDesciption& zaxis, const std::string& zaxisname)
 }
 
 int
-zaxis_from_name(const std::string& zaxisname)
+zaxis_from_name(const std::string &zaxisname)
 {
   int zaxisID = CDI_UNDEFID;
 
diff --git a/src/cdo_zaxis.h b/src/cdo_zaxis.h
index 030a5c694e6118a1caed2bd2d5127536d38eeae0..8c1fc2893bb96caf6d790dfc1e0ad585ea42d4ab 100644
--- a/src/cdo_zaxis.h
+++ b/src/cdo_zaxis.h
@@ -3,18 +3,19 @@
 
 #include <string>
 #include "varray.h"
+#include "cdi.h"
 
-int cdo_define_zaxis(const std::string& zaxisfile);
-void define_zaxis(const char* zaxisarg);
-int zaxis_from_name(const std::string& zaxisname);
-int zaxis_from_file(FILE* zfp, const char* filename);
+int cdo_define_zaxis(const std::string &zaxisfile);
+void define_zaxis(const char *zaxisarg);
+int zaxis_from_name(const std::string &zaxisname);
+int zaxis_from_file(FILE *zfp, const char *filename);
 int zaxis_to_ltype(int zaxisID);
 double cdo_zaxis_inq_level(int zaxisID, int levelID);
-int cdo_zaxis_inq_levels(int zaxisID, double* levels);
+int cdo_zaxis_inq_levels(int zaxisID, double *levels);
 
-void gen_layer_bounds(int nlev, const Varray<double>& levels, Varray<double>& lbounds, Varray<double>& ubounds);
-int get_layer_thickness(bool useWeights, bool genBounds, int index, int zaxisID, int nlev, Varray<double>& thickness,
-                        Varray<double>& weights);
+void gen_layer_bounds(int nlev, const Varray<double> &levels, Varray<double> &lbounds, Varray<double> &ubounds);
+int get_layer_thickness(bool useWeights, bool genBounds, int index, int zaxisID, int nlev, Varray<double> &thickness,
+                        Varray<double> &weights);
 
 static inline bool
 positive_is_down(int zaxisID)
diff --git a/src/cdotest.cc b/src/cdotest.cc
index 29b565e49886da71d05325c0273e418a3ab92e3f..e0c5b1afed1f8397b56cfffd6d3178a26d0038d4 100644
--- a/src/cdotest.cc
+++ b/src/cdotest.cc
@@ -62,8 +62,8 @@ read_file(const char path[], Varray2D<double> &vars, int nvars, int nts)
 
       // auto vDateTime = taxisInqVdatetime(taxisID);
 
-      size_t nmiss;
-      for (int varID = 0; varID < nvars; ++varID) streamReadVar(streamID, varID, &vars[varID][tsID], &nmiss);
+      size_t numMissVals;
+      for (int varID = 0; varID < nvars; ++varID) streamReadVar(streamID, varID, &vars[varID][tsID], &numMissVals);
     }
 
   streamClose(streamID);
@@ -114,9 +114,9 @@ write_file(const char path[], const double array[], int length)
       streamDefTimestep(streamID, tsID);
 
       auto value = array[tsID];
-      size_t nmiss = dbl_is_equal(value, MISSVAL) ? 1 : 0;
+      size_t numMissVals = dbl_is_equal(value, MISSVAL) ? 1 : 0;
 
-      streamWriteVar(streamID, varID, &value, nmiss);
+      streamWriteVar(streamID, varID, &value, numMissVals);
     }
 
   streamClose(streamID);
diff --git a/src/cmortable_parser.cc b/src/cmortable_parser.cc
index 6c0ddeca175e83da2a6552ba744eadea270742a2..19d755be2ce967ec45c6836f8b48ea02c1db6f09 100644
--- a/src/cmortable_parser.cc
+++ b/src/cmortable_parser.cc
@@ -51,9 +51,9 @@ readLineFromBuffer(char *buffer, size_t *buffersize, char *line, size_t len)
 static char *
 skipSeparator(char *pline)
 {
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
   if (*pline == '=' || *pline == ':') pline++;
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
 
   return pline;
 }
@@ -61,10 +61,10 @@ skipSeparator(char *pline)
 static char *
 getElementName(char *pline, char *name)
 {
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
   auto len = strlen(pline);
   size_t pos = 0;
-  while (pos < len && !isspace((int) *(pline + pos)) && *(pline + pos) != '=' && *(pline + pos) != ':') pos++;
+  while (pos < len && !std::isspace((int) *(pline + pos)) && *(pline + pos) != '=' && *(pline + pos) != ':') pos++;
 
   strncpy(name, pline, pos);
   name[pos] = 0;
@@ -76,7 +76,7 @@ getElementName(char *pline, char *name)
 static char *
 getElementValue(char *pline)
 {
-  while (isspace((int) *pline)) pline++;
+  while (std::isspace((int) *pline)) pline++;
   auto len = strlen(pline);
   if (*pline != '"' && *pline != '\'')
     for (size_t i = 1; i < len; ++i)
@@ -86,7 +86,7 @@ getElementValue(char *pline)
           len = i;
           break;
         }
-  while (isspace((int) *(pline + len - 1)) && len)
+  while (std::isspace((int) *(pline + len - 1)) && len)
     {
       *(pline + len - 1) = 0;
       len--;
@@ -108,7 +108,7 @@ parse_cmortable_buffer(PMList &pmlist, size_t buffersize, char *buffer)
     {
       // linenumber++;
       auto pline = line;
-      while (isspace((int) *pline)) pline++;
+      while (std::isspace((int) *pline)) pline++;
       if (*pline == '#' || *pline == '!' || *pline == '\0') continue;
       //  len = (int) strlen(pline);
 
diff --git a/src/compare.h b/src/compare.h
index d1bc0089d9a6a510988fe71ba10b6090932e0b1e..465967936c7a240aaeaaa4c8aff492dd31376e91 100644
--- a/src/compare.h
+++ b/src/compare.h
@@ -12,16 +12,15 @@ const auto is_equal     = [](auto a, auto b) noexcept { return !(a < b || b < a)
 const auto dbl_is_equal = [](auto a, auto b) noexcept { return (std::isnan(a) || std::isnan(b) ? (std::isnan(a) && std::isnan(b)) : is_equal(a, b)); };
 // clang-format on
 
-
-//#define DBL_IS_EQUAL(x,y) (!(x < y || y < x))
-//#define DBL_IS_EQUAL(x, y) (std::isnan(x) || std::isnan(y) ? (std::isnan(x) && std::isnan(y) ? 1 : 0) : !(x < y || y < x))
+// #define DBL_IS_EQUAL(x,y) (!(x < y || y < x))
+// #define DBL_IS_EQUAL(x, y) (std::isnan(x) || std::isnan(y) ? (std::isnan(x) && std::isnan(y) ? 1 : 0) : !(x < y || y < x))
 static inline bool
 DBL_IS_EQUAL(double x, double y)
 {
   return (std::isnan(x) || std::isnan(y) ? (std::isnan(x) && std::isnan(y)) : !(x < y || y < x));
 }
 
-//#define IS_EQUAL(x, y) (!IS_NOT_EQUAL(x, y))
+// #define IS_EQUAL(x, y) (!IS_NOT_EQUAL(x, y))
 constexpr bool
 IS_NOT_EQUAL(double x, double y) noexcept
 {
@@ -45,5 +44,10 @@ cdo_cmpstr(const std::string &lhs, const std::string &rhs)
 {
   return (lhs.compare(rhs) == 0);
 }
+static inline bool
+cdo_cmpstr(const std::string_view &lhs, const std::string &rhs)
+{
+  return (lhs.compare(rhs) == 0);
+}
 
 #endif /* COMPARE_H */
diff --git a/src/constants.h b/src/constants.h
index c61cdba7467880af111e3e265dc254cb34025e2d..4536cc3da1a2f0d327a0c1e40a6adb90a8849839 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -17,7 +17,7 @@ constexpr double C_EARTH_GRAV = 9.80665;
 #define C_RG (1.0 / PlanetGrav)
 
 constexpr double C_RCPV = 4.0 * C_RV;
-#define C_RETV (C_RV / PlanetRD - 1.)
+#define C_RETV (C_RV / PlanetRD - 1.0)
 constexpr double C_RCW = 4218.;        // specific water heat capacity ??
 constexpr double C_RCS = 2106.;        // specific ice heat capacity ??
 constexpr double C_RTT = 273.16;       // melting temperature of ice/snow
@@ -28,6 +28,10 @@ constexpr double C_RESTT = 611.14;
 
 constexpr double C_TIMES_RHOH2O = -333700000.0;
 
+constexpr double PlanetRDDefault = C_EARTH_RD;
+constexpr double PlanetRadiusDefault = C_EARTH_RADIUS;
+constexpr double PlanetGravDefault = C_EARTH_GRAV;
+
 extern double PlanetRD;
 extern double PlanetRadius;
 extern double PlanetGrav;
diff --git a/src/datarangelist.h b/src/datarangelist.h
index cf497a18eeabc2a9614c7b8b3024f8b9f1224d61..278aa131efc8ccabf7f619a020f18519f2895ce3 100644
--- a/src/datarangelist.h
+++ b/src/datarangelist.h
@@ -26,10 +26,10 @@ struct Datarange
 
   template <typename T>
   void
-  check_datarange(T *array, size_t nmiss)
+  check_datarange(T *array, size_t numMissVals)
   {
-    const auto mm = nmiss ? varray_min_max_mv(gridsize, array, (T) missval) : varray_min_max(gridsize, array);
-    const auto numberOfValues = nmiss ? mm.n : gridsize;
+    const auto mm = numMissVals ? varray_min_max_mv(gridsize, array, (T) missval) : varray_min_max(gridsize, array);
+    const auto numberOfValues = numMissVals ? mm.n : gridsize;
 
     if (numberOfValues > 0)
       {
diff --git a/src/datetime.cc b/src/datetime.cc
index 09a3568473d67ff5aefc1c83152f07863b89f9cb..e1b41d9116448edc73ff69c209ee7c02b8e91f87 100644
--- a/src/datetime.cc
+++ b/src/datetime.cc
@@ -36,18 +36,18 @@ time_units_cstr(TimeUnits timeUnit)
 }
 
 void
-set_timestat_date(const std::string &optarg)
+set_timestat_date(const std::string &p_optarg)
 {
   TimeStat timestatdate = TimeStat::UNDEF;
 
   // clang-format off
-  if      (optarg == "first")   timestatdate = TimeStat::FIRST;
-  else if (optarg == "last")    timestatdate = TimeStat::LAST;
-  else if (optarg == "middle")  timestatdate = TimeStat::MEAN;
-  else if (optarg == "midhigh") timestatdate = TimeStat::MIDHIGH;
+  if      (p_optarg == "first")   timestatdate = TimeStat::FIRST;
+  else if (p_optarg == "last")    timestatdate = TimeStat::LAST;
+  else if (p_optarg == "middle")  timestatdate = TimeStat::MEAN;
+  else if (p_optarg == "midhigh") timestatdate = TimeStat::MIDHIGH;
   // clang-format on
 
-  if (timestatdate == TimeStat::UNDEF) cdo_abort("option --%s: unsupported argument: %s", "timestat_date", optarg);
+  if (timestatdate == TimeStat::UNDEF) cdo_abort("option --%s: unsupported argument: %s", "timestat_date", p_optarg);
 
   CDO_Timestat_Date = timestatdate;
 }
@@ -186,10 +186,7 @@ DateTimeList::mean(int nsteps)
       this->timestat.v = julianDate_decode(this->calendar, julianDatem);
 #endif
     }
-  else
-    {
-      this->timestat.v = this->dtInfo[nsteps / 2].v;
-    }
+  else { this->timestat.v = this->dtInfo[nsteps / 2].v; }
 }
 
 void
@@ -250,10 +247,7 @@ datetime_avg(int calendar, int ndates, const std::vector<CdiDateTime> &cdiDateTi
       auto julianDatem = julianDate_add_seconds(julianDate1, std::lround(seconds));
       return julianDate_decode(calendar, julianDatem);
     }
-  else
-    {
-      return cdiDateTimes[ndates / 2];
-    }
+  else { return cdiDateTimes[ndates / 2]; }
 }
 
 void
@@ -307,31 +301,16 @@ get_time_increment(double jdelta, CdiDate vDate0, CdiDate vDate1)
   if (deltam == 0) deltam = 1;
 
   TimeIncrement timeIncr;
-  if (seconds >= (3600 * 24 * 30 * 12))
-    {
-      timeIncr = { deltay, TimeUnits::YEARS };
-    }
-  else if (seconds >= (3600 * 24 * 30) && seconds / (3600 * 24 * 30) < 12)
-    {
-      timeIncr = { deltam, TimeUnits::MONTHS };
-    }
+  if (seconds >= (3600 * 24 * 30 * 12)) { timeIncr = { deltay, TimeUnits::YEARS }; }
+  else if (seconds >= (3600 * 24 * 30) && seconds / (3600 * 24 * 30) < 12) { timeIncr = { deltam, TimeUnits::MONTHS }; }
   else if (seconds >= (3600 * 24) && seconds / (3600 * 24) < 32)
     {
       timeIncr = { seconds / (3600 * 24), TimeUnits::DAYS };
       if (timeIncr.period >= 27 && deltam == 1) timeIncr = { 1, TimeUnits::MONTHS };
     }
-  else if (seconds >= 3600 && seconds % 3600 == 0)
-    {
-      timeIncr = { seconds / 3600, TimeUnits::HOURS };
-    }
-  else if (seconds >= 60 && seconds % 60 == 0)
-    {
-      timeIncr = { seconds / 60, TimeUnits::MINUTES };
-    }
-  else
-    {
-      timeIncr = { seconds, TimeUnits::SECONDS };
-    }
+  else if (seconds >= 3600 && seconds % 3600 == 0) { timeIncr = { seconds / 3600, TimeUnits::HOURS }; }
+  else if (seconds >= 60 && seconds % 60 == 0) { timeIncr = { seconds / 60, TimeUnits::MINUTES }; }
+  else { timeIncr = { seconds, TimeUnits::SECONDS }; }
 
   timeIncr.period *= sign;
 
@@ -470,10 +449,7 @@ decode_datestring(const std::string &dateString)
       std::sscanf(dateString.c_str(), "%d-%d-%d", &year, &month, &day);
       return cdiDate_encode(year, month, day);
     }
-  else
-    {
-      return cdiDate_set(parameter_to_long(dateString));
-    }
+  else { return cdiDate_set(parameter_to_long(dateString)); }
 }
 
 CdiTime
@@ -488,10 +464,7 @@ decode_timestring(const std::string &timeString)
       ms = (fseconds - second) * 1000;
       return cdiTime_encode(hour, minute, second, ms);
     }
-  else
-    {
-      return cdiTime_set(parameter_to_int(timeString));
-    }
+  else { return cdiTime_set(parameter_to_int(timeString)); }
 }
 
 void
diff --git a/src/dcw_reader.cc b/src/dcw_reader.cc
index 2c1349ab0e4039b54402a0cf2b7d58b8a7f0e0a5..ffb4a59dd47b93585c3dc90c58f8faa976fcc411 100644
--- a/src/dcw_reader.cc
+++ b/src/dcw_reader.cc
@@ -45,7 +45,7 @@ dcw_get_path(const char *name, const char *suffix, char *path)
 
   if (DCW_Dir)
     {
-      sprintf(path, "%s/%s%s", DCW_Dir, name, suffix);
+      std::snprintf(path, PATH_MAX, "%s/%s%s", DCW_Dir, name, suffix);
       if (access(path, R_OK) == 0) found = true;
     }
   else
@@ -184,7 +184,7 @@ dcw_find_country(const std::string &code, const std::vector<DCW_Country> &list)
 static int
 dcw_find_state(const std::string &code, const std::vector<DCW_State> &list)
 {
-  auto country = code.substr(0,2);
+  auto country = code.substr(0, 2);
   auto state = code.substr(2);
   for (size_t i = 0, n = list.size(); i < n; ++i)
     {
@@ -543,7 +543,7 @@ dcw_print_polygons(const DCW_Lists &dcw_lists, const std::vector<std::string> &c
 void
 dcw_sort_countries(DCW_Lists &dcw_lists)
 {
-  std::sort(dcw_lists.countries.begin(), dcw_lists.countries.end(), compare_code);
+  std::ranges::sort(dcw_lists.countries, compare_code);
 }
 
 /*
diff --git a/src/ecacore.cc b/src/ecacore.cc
index ea8863c9ff31ff6f05158ae30e13ff34433416e0..f00e7f1e521b1dc6ef53114cdefffa5de7b45dd8 100644
--- a/src/ecacore.cc
+++ b/src/ecacore.cc
@@ -163,7 +163,7 @@ eca1(const ECA_REQUEST_1 &request)
                   if (request.var1.f2 != &vfarnum2)
                     {
                       field_fill(var12[levelID], missval);
-                      var12[levelID].nmiss = gridsize;
+                      var12[levelID].numMissVals = gridsize;
                     }
                   field_fill(samp1[levelID], missval);
                   if (!samp2[levelID].empty()) field_fill(samp2[levelID], 0.0);
@@ -171,13 +171,13 @@ eca1(const ECA_REQUEST_1 &request)
                   if (IS_SET(request.var2.h2)) field_fill(var21[levelID], missval);
                   if (IS_SET(request.var2.h3)) field_fill(var23[levelID], missval);
 
-                  samp1[levelID].nmiss = gridsize;
-                  if (IS_SET(request.var1.f3)) var13[levelID].nmiss = gridsize;
-                  if (IS_SET(request.var2.h2)) var21[levelID].nmiss = gridsize;
-                  if (IS_SET(request.var2.h3)) var23[levelID].nmiss = gridsize;
+                  samp1[levelID].numMissVals = gridsize;
+                  if (IS_SET(request.var1.f3)) var13[levelID].numMissVals = gridsize;
+                  if (IS_SET(request.var2.h2)) var21[levelID].numMissVals = gridsize;
+                  if (IS_SET(request.var2.h3)) var23[levelID].numMissVals = gridsize;
                 }
 
-              cdo_read_record(istreamID, field1.vec_d.data(), &field1.nmiss);
+              cdo_read_record(istreamID, field1.vec_d.data(), &field1.numMissVals);
               field1.grid = var12[levelID].grid;
               field1.missval = var12[levelID].missval;
 
@@ -186,14 +186,14 @@ eca1(const ECA_REQUEST_1 &request)
               if (IS_SET(request.var2.h2))
                 {
                   field2.vec_d = field1.vec_d;
-                  field2.nmiss = field1.nmiss;
+                  field2.numMissVals = field1.numMissVals;
                   field2.grid = field1.grid;
                   field2.missval = field1.missval;
                 }
 
               if (IS_SET(request.var1.f1)) request.var1.f1(field1, request.var1.f1arg);
 
-              if (field1.nmiss || !samp2[levelID].empty())
+              if (field1.numMissVals || !samp2[levelID].empty())
                 {
                   if (samp2[levelID].empty())
                     {
@@ -213,7 +213,7 @@ eca1(const ECA_REQUEST_1 &request)
               if (IS_SET(request.var1.f3) && request.var1.f2 == &vfarnum2)
                 {
                   varray_copy(gridsize, var12[levelID].vec_d, field3.vec_d);
-                  field3.nmiss = var12[levelID].nmiss;
+                  field3.numMissVals = var12[levelID].numMissVals;
                   field3.grid = var12[levelID].grid;
                   field3.missval = var12[levelID].missval;
                 }
@@ -226,7 +226,7 @@ eca1(const ECA_REQUEST_1 &request)
                   if (IS_NOT_SET(request.var2.h2))
                     {
                       varray_copy(gridsize, var12[levelID].vec_d, field2.vec_d);
-                      field2.nmiss = var12[levelID].nmiss;
+                      field2.numMissVals = var12[levelID].numMissVals;
                       field2.grid = var12[levelID].grid;
                       field2.missval = var12[levelID].missval;
                     }
@@ -253,7 +253,7 @@ eca1(const ECA_REQUEST_1 &request)
                       auto len = field1.size;
                       if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-                      if (field2.nmiss)
+                      if (field2.numMissVals)
                         {
                           for (size_t i = 0; i < len; ++i)
                             {
@@ -337,7 +337,7 @@ eca1(const ECA_REQUEST_1 &request)
           vfarsel(rvar, samp1[levelID]);
 
           cdo_def_record(ostreamID, varID, levelID);
-          cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.nmiss);
+          cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.numMissVals);
         }
 
       if (IS_SET(request.var2.h2) || IS_SET(request.var2.h3))
@@ -350,7 +350,7 @@ eca1(const ECA_REQUEST_1 &request)
               vfarsel(rvar, samp1[levelID]);
 
               cdo_def_record(ostreamID, varID, levelID);
-              cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.nmiss);
+              cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.numMissVals);
             }
         }
 
@@ -469,7 +469,7 @@ eca2(const ECA_REQUEST_2 &request)
           cdo_inq_record(istreamID2, &varID, &levelID);
           if (varID != FIRST_VAR_ID) continue;
           auto &rvar = vars2[dayOfYear][0][levelID];
-          cdo_read_record(istreamID2, rvar.vec_d.data(), &rvar.nmiss);
+          cdo_read_record(istreamID2, rvar.vec_d.data(), &rvar.numMissVals);
         }
 
       itsID++;
@@ -512,7 +512,7 @@ eca2(const ECA_REQUEST_2 &request)
                   if (request.var1.f4 != &vfarnum2)
                     {
                       field_fill(var14[levelID], missval1);
-                      var14[levelID].nmiss = gridsize;
+                      var14[levelID].numMissVals = gridsize;
                     }
                   field_fill(samp1[levelID], missval1);
                   field_fill(samp2[levelID], missval1);
@@ -521,18 +521,18 @@ eca2(const ECA_REQUEST_2 &request)
                   if (IS_SET(request.var1.f5)) field_fill(var15[levelID], missval1);
                   if (IS_SET(request.var2.h2)) field_fill(var22[levelID], missval1);
 
-                  samp1[levelID].nmiss = gridsize;
-                  samp2[levelID].nmiss = gridsize;
-                  if (request.var1.epilog == PERCENT_OF_TOTAL_AMOUNT) total[levelID].nmiss = gridsize;
-                  if (IS_SET(request.var1.f5)) var15[levelID].nmiss = gridsize;
-                  if (IS_SET(request.var2.h2)) var22[levelID].nmiss = gridsize;
+                  samp1[levelID].numMissVals = gridsize;
+                  samp2[levelID].numMissVals = gridsize;
+                  if (request.var1.epilog == PERCENT_OF_TOTAL_AMOUNT) total[levelID].numMissVals = gridsize;
+                  if (IS_SET(request.var1.f5)) var15[levelID].numMissVals = gridsize;
+                  if (IS_SET(request.var2.h2)) var22[levelID].numMissVals = gridsize;
                 }
 
-              cdo_read_record(istreamID1, field1.vec_d.data(), &field1.nmiss);
+              cdo_read_record(istreamID1, field1.vec_d.data(), &field1.numMissVals);
               field1.grid = gridID;
               field1.missval = missval1;
               field2.grid = vars2[dayOfYear][0][levelID].grid;
-              field2.nmiss = vars2[dayOfYear][0][levelID].nmiss;
+              field2.numMissVals = vars2[dayOfYear][0][levelID].numMissVals;
               field2.missval = missval2;
               field_copy(vars2[dayOfYear][0][levelID], field2);
 
@@ -544,7 +544,7 @@ eca2(const ECA_REQUEST_2 &request)
               if (IS_SET(request.var1.f1)) request.var1.f1(field1, request.var1.f1arg);
               if (IS_SET(request.var1.f2)) request.var1.f2(field2, request.var1.f2arg);
 
-              if (field1.nmiss || !samp3[levelID].empty())
+              if (field1.numMissVals || !samp3[levelID].empty())
                 {
                   if (samp3[levelID].empty())
                     {
@@ -561,7 +561,7 @@ eca2(const ECA_REQUEST_2 &request)
               if (IS_SET(request.var1.f5) && request.var1.f4 == &vfarnum2)
                 {
                   varray_copy(gridsize, var14[levelID].vec_d, field3.vec_d);
-                  field3.nmiss = var14[levelID].nmiss;
+                  field3.numMissVals = var14[levelID].numMissVals;
                   field3.grid = var14[levelID].grid;
                   field3.missval = var14[levelID].missval;
                 }
@@ -572,7 +572,7 @@ eca2(const ECA_REQUEST_2 &request)
               if (IS_SET(request.var2.h2))
                 {
                   varray_copy(gridsize, var14[levelID].vec_d, field2.vec_d);
-                  field2.nmiss = var14[levelID].nmiss;
+                  field2.numMissVals = var14[levelID].numMissVals;
                   field2.grid = var14[levelID].grid;
                   field2.missval = var14[levelID].missval;
 
@@ -592,7 +592,7 @@ eca2(const ECA_REQUEST_2 &request)
                       auto len = field1.size;
                       if (len != field3.size) cdo_abort("Fields have different size (%s)", __func__);
 
-                      if (field1.nmiss)
+                      if (field1.numMissVals)
                         {
                           for (size_t i = 0; i < len; ++i)
                             {
@@ -684,7 +684,7 @@ eca2(const ECA_REQUEST_2 &request)
           vfarsel(rvar, samp2[levelID]);
 
           cdo_def_record(ostreamID, varID, levelID);
-          cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.nmiss);
+          cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.numMissVals);
         }
 
       if (IS_SET(request.var2.h2))
@@ -698,7 +698,7 @@ eca2(const ECA_REQUEST_2 &request)
               vfarsel(rvar, samp2[levelID]);
 
               cdo_def_record(ostreamID, varID, levelID);
-              cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.nmiss);
+              cdo_write_record(ostreamID, rvar.vec_d.data(), rvar.numMissVals);
             }
         }
 
@@ -823,15 +823,15 @@ eca3(const ECA_REQUEST_3 &request)
                       var1[levelID].vec_d[i] = missval;
                       var2[levelID].vec_d[i] = missval;
                     }
-                  var1[levelID].nmiss = gridsize;
-                  var2[levelID].nmiss = gridsize;
+                  var1[levelID].numMissVals = gridsize;
+                  var2[levelID].numMissVals = gridsize;
                 }
 
-              cdo_read_record(istreamID1, field1.vec_d.data(), &field1.nmiss);
+              cdo_read_record(istreamID1, field1.vec_d.data(), &field1.numMissVals);
               field1.grid = var1[levelID].grid;
               field1.missval = var1[levelID].missval;
 
-              cdo_read_record(istreamID2, field2.vec_d.data(), &field2.nmiss);
+              cdo_read_record(istreamID2, field2.vec_d.data(), &field2.numMissVals);
               field2.grid = var1[levelID].grid;
               field2.missval = var1[levelID].missval;
 
@@ -867,7 +867,7 @@ eca3(const ECA_REQUEST_3 &request)
       for (int levelID = 0; levelID < nlevels; ++levelID)
         {
           cdo_def_record(ostreamID, varID, levelID);
-          cdo_write_record(ostreamID, var1[levelID].vec_d.data(), var1[levelID].nmiss);
+          cdo_write_record(ostreamID, var1[levelID].vec_d.data(), var1[levelID].numMissVals);
         }
 
       if (nrecs == 0) break;
@@ -885,7 +885,7 @@ fldhvs(const FieldVector &fieldVector, const size_t nlevels)
 {
   for (size_t level = 0; level < nlevels; level++)
     {
-      if (fieldVector[level].nmiss != fieldVector[level].size) return true;
+      if (fieldVector[level].numMissVals != fieldVector[level].size) return true;
     }
 
   return false;
@@ -1008,7 +1008,7 @@ eca4(const ECA_REQUEST_4 &request)
     {
       int levelID;
       cdo_inq_record(istreamID2, &varID, &levelID);
-      cdo_read_record(istreamID2, mask.vec_d.data(), &mask.nmiss);
+      cdo_read_record(istreamID2, mask.vec_d.data(), &mask.numMissVals);
       mask.grid = gridID;
       mask.missval = vlistInqVarMissval(ivlistID2, 0);
 
@@ -1057,11 +1057,11 @@ eca4(const ECA_REQUEST_4 &request)
                   field_fill(startDateWithHist[0][levelID], missval);
                   field_fill(endDateWithHist[0][levelID], missval);
 
-                  gslDuration[levelID].nmiss = 0;
-                  gslFirstDay[levelID].nmiss = 0;
+                  gslDuration[levelID].numMissVals = 0;
+                  gslFirstDay[levelID].numMissVals = 0;
                   // reinitialize the current year
-                  startDateWithHist[0][levelID].nmiss = gridsize;
-                  endDateWithHist[0][levelID].nmiss = gridsize;
+                  startDateWithHist[0][levelID].numMissVals = gridsize;
+                  endDateWithHist[0][levelID].numMissVals = gridsize;
                 }
               // init the history ONCE
               if (0 == itsID)
@@ -1069,13 +1069,13 @@ eca4(const ECA_REQUEST_4 &request)
                   field_fill(startDateWithHist[1][levelID], missval);
                   field_fill(endDateWithHist[1][levelID], missval);
 
-                  startDateWithHist[1][levelID].nmiss = gridsize;
-                  endDateWithHist[1][levelID].nmiss = gridsize;
+                  startDateWithHist[1][levelID].numMissVals = gridsize;
+                  endDateWithHist[1][levelID].numMissVals = gridsize;
                 }
 
-              cdo_read_record(istreamID1, fieldGt.vec_d.data(), &fieldGt.nmiss);
+              cdo_read_record(istreamID1, fieldGt.vec_d.data(), &fieldGt.numMissVals);
               fieldLt.vec_d = fieldGt.vec_d;
-              fieldLt.nmiss = fieldGt.nmiss;
+              fieldLt.numMissVals = fieldGt.numMissVals;
               fieldGt.grid = startCount[levelID].grid;
               fieldGt.missval = startCount[levelID].missval;
               fieldLt.grid = startCount[levelID].grid;
@@ -1091,7 +1091,7 @@ eca4(const ECA_REQUEST_4 &request)
                         if (!dbl_is_equal(startCount[levelID].vec_d[i], missval))
                           {
                             startCount[levelID].vec_d[i] = missval;
-                            startCount[levelID].nmiss++;
+                            startCount[levelID].numMissVals++;
                           }
                     }
                   // reset southern endCount
@@ -1101,7 +1101,7 @@ eca4(const ECA_REQUEST_4 &request)
                         if (!dbl_is_equal(endCount[levelID].vec_d[i], missval))
                           {
                             endCount[levelID].vec_d[i] = missval;
-                            endCount[levelID].nmiss++;
+                            endCount[levelID].numMissVals++;
                           }
                     }
 
@@ -1125,7 +1125,7 @@ eca4(const ECA_REQUEST_4 &request)
                               if (!dbl_is_equal(endCount[levelID].vec_d[i], missval))
                                 {
                                   endCount[levelID].vec_d[i] = missval;
-                                  endCount[levelID].nmiss++;
+                                  endCount[levelID].numMissVals++;
                                 }
                             }
                         }
@@ -1142,7 +1142,7 @@ eca4(const ECA_REQUEST_4 &request)
                               if (!dbl_is_equal(startCount[levelID].vec_d[i], missval))
                                 {
                                   startCount[levelID].vec_d[i] = missval;
-                                  startCount[levelID].nmiss++;
+                                  startCount[levelID].numMissVals++;
                                 }
                             }
                         }
@@ -1236,13 +1236,13 @@ eca4(const ECA_REQUEST_4 &request)
                         }
                     }
                 }
-              // update nmiss for saving data in GRIB
-              startCount[levelID].nmiss = field_num_miss(startCount[levelID]);
-              endCount[levelID].nmiss = field_num_miss(endCount[levelID]);
-              startDateWithHist[1][levelID].nmiss = field_num_miss(startDateWithHist[1][levelID]);
-              startDateWithHist[0][levelID].nmiss = field_num_miss(startDateWithHist[0][levelID]);
-              endDateWithHist[1][levelID].nmiss = field_num_miss(endDateWithHist[1][levelID]);
-              endDateWithHist[0][levelID].nmiss = field_num_miss(endDateWithHist[0][levelID]);
+              // update numMissVals for saving data in GRIB
+              startCount[levelID].numMissVals = field_num_miss(startCount[levelID]);
+              endCount[levelID].numMissVals = field_num_miss(endCount[levelID]);
+              startDateWithHist[1][levelID].numMissVals = field_num_miss(startDateWithHist[1][levelID]);
+              startDateWithHist[0][levelID].numMissVals = field_num_miss(startDateWithHist[0][levelID]);
+              endDateWithHist[1][levelID].numMissVals = field_num_miss(endDateWithHist[1][levelID]);
+              endDateWithHist[0][levelID].numMissVals = field_num_miss(endDateWithHist[0][levelID]);
             }
 
           ovDateTime = ivDateTime;
diff --git a/src/ecautil.cc b/src/ecautil.cc
index 483e1c141d84cfebeab841e8d4199aa74cb8d1b9..f2970b504616978d4aca569897094151a38631c9 100644
--- a/src/ecautil.cc
+++ b/src/ecautil.cc
@@ -19,7 +19,7 @@
 #include "ecautil.h"
 
 /**
- * Counts the number of nonmissing values. The result of the operation
+ * Counts the number of nonumMissValsing values. The result of the operation
  * is computed according to the following rules:
  *
  * field1  field2  mode  result
@@ -53,7 +53,7 @@ count(Field &field1, const Field &field2, double mode)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss)
+  if (field1.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         {
@@ -81,11 +81,11 @@ count(Field &field1, const Field &field2, double mode)
             }
         }
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
   else
     {
-      if (field2.nmiss)
+      if (field2.numMissVals)
         {
           for (size_t i = 0; i < len; ++i)
             {
@@ -138,7 +138,7 @@ selcomp(Field &field1, const Field &field2, int (*compare)(double, double))
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (dbl_is_equal(array1[i], missval1) || dbl_is_equal(array2[i], missval2) || !compare(array1[i], array2[i]))
@@ -150,7 +150,7 @@ selcomp(Field &field1, const Field &field2, int (*compare)(double, double))
         if (!compare(array1[i], array2[i])) array1[i] = missval1;
     }
 
-  field1.nmiss = field_num_miss(field1);
+  field1.numMissVals = field_num_miss(field1);
 }
 
 /**
@@ -180,7 +180,7 @@ selcompc(Field &field, double c, int (*compare)(double, double))
     {
       for (size_t i = 0; i < len; ++i) array[i] = missval;
     }
-  else if (field.nmiss)
+  else if (field.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (dbl_is_equal(array[i], missval) || !compare(array[i], c)) array[i] = missval;
@@ -191,7 +191,7 @@ selcompc(Field &field, double c, int (*compare)(double, double))
         if (!compare(array[i], c)) array[i] = missval;
     }
 
-  field.nmiss = field_num_miss(field);
+  field.numMissVals = field_num_miss(field);
 }
 
 static int
@@ -259,7 +259,7 @@ vfarsel(Field &field1, const Field &field2)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different gridsize (%s)", __func__);
 
-  if (field2.nmiss)
+  if (field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (dbl_is_equal(array2[i], missval2) || dbl_is_equal(array2[i], 0.0)) array1[i] = missval1;
@@ -270,7 +270,7 @@ vfarsel(Field &field1, const Field &field2)
         if (is_equal(array2[i], 0.0)) array1[i] = missval1;
     }
 
-  field1.nmiss = field_num_miss(field1);
+  field1.numMissVals = field_num_miss(field1);
 }
 
 void
@@ -493,8 +493,8 @@ compute_gsl(int nlevels, size_t gridsize, std::vector<double> &yvals, double mis
 
   for (int levelID = 0; levelID < nlevels; ++levelID)
     {
-      gslDuration[levelID].nmiss = field_num_miss(gslDuration[levelID]);
-      gslFirstDay[levelID].nmiss = field_num_miss(gslFirstDay[levelID]);
+      gslDuration[levelID].numMissVals = field_num_miss(gslDuration[levelID]);
+      gslFirstDay[levelID].numMissVals = field_num_miss(gslFirstDay[levelID]);
     }
 }
 
@@ -511,12 +511,12 @@ write_gsl_stream(CdoStreamID ostreamID, int otaxisID, int otsID, int ovarID1, in
   for (int levelID = 0; levelID < nlevels; ++levelID)
     {
       cdo_def_record(ostreamID, ovarID1, levelID);
-      cdo_write_record(ostreamID, gslDuration[levelID].vec_d.data(), gslDuration[levelID].nmiss);
+      cdo_write_record(ostreamID, gslDuration[levelID].vec_d.data(), gslDuration[levelID].numMissVals);
     }
 
   for (int levelID = 0; levelID < nlevels; ++levelID)
     {
       cdo_def_record(ostreamID, ovarID2, levelID);
-      cdo_write_record(ostreamID, gslFirstDay[levelID].vec_d.data(), gslFirstDay[levelID].nmiss);
+      cdo_write_record(ostreamID, gslFirstDay[levelID].vec_d.data(), gslFirstDay[levelID].numMissVals);
     }
 }
diff --git a/src/ecautil.h b/src/ecautil.h
index be0d35cb2ae087f73712ef5662c965d4a3ba083f..b72a5d108f6790a347a3f1ceaf10cabdfd45bdde 100644
--- a/src/ecautil.h
+++ b/src/ecautil.h
@@ -10,6 +10,7 @@
 #define ECAUTIL_H_
 
 #include "field.h"
+#include "cdoStream.h"
 
 /**
  * Computes the day-of-year correspnding a given Gregorian date.
@@ -21,7 +22,7 @@
 unsigned long day_of_year(int date);
 
 /**
- * Counts the number of nonmissing values in a field. The result
+ * Counts the number of nonumMissValsing values in a field. The result
  * of the operation is computed according to the following rules:
  *
  * field1  field2  result
@@ -36,7 +37,7 @@ unsigned long day_of_year(int date);
 void vfarnum(Field &field1, const Field &field2);
 
 /**
- * Counts the number of consecutive nonmissing values in a field.
+ * Counts the number of consecutive nonumMissValsing values in a field.
  * The result of the operation is computed according to the following
  * rules:
  *
@@ -53,7 +54,7 @@ void vfarnum2(Field &field1, const Field &field2);
 
 /**
  * Counts the number of values in series of at least n consecutive
- * nonmissing values. The result of the operation is computed according
+ * nonumMissValsing values. The result of the operation is computed according
  * to the following rules:
  *
  * field1  field2  result
diff --git a/src/expr.cc b/src/expr.cc
index 694f821e8258289201f03f327b99d1d2a15896d1..f5a376fb74353d8f28eae97f95d6ca55d588c4e5 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -260,7 +260,7 @@ param_meta_copy(ParamEntry &out, const ParamEntry &in)
   out.nlat = in.nlat;
   out.nlev = in.nlev;
   out.missval = in.missval;
-  out.nmiss = 0;
+  out.numMissVals = 0;
   out.coord = 0;
   out.hasMV = true;
   out.name = "";
@@ -274,7 +274,7 @@ expr_con_var(int init, int oper, const nodeType *p1, const nodeType *p2)
 {
   auto ngp = (p2->param.ngp > 0) ? p2->param.ngp : 1;
   auto nlev = (p2->param.nlev > 0) ? p2->param.nlev : 1;
-  auto hasMV = (p2->param.nmiss > 0);
+  auto hasMV = (p2->param.numMissVals > 0);
   auto datatype = p2->param.datatype;
   auto missval2 = p2->param.missval;
 
@@ -298,7 +298,7 @@ expr_con_var(int init, int oper, const nodeType *p1, const nodeType *p2)
 
       oper_expr_con_var(oper, hasMV, n, missval2, odat, cval, idat);
 
-      p->param.nmiss = array_num_mv(n, odat, missval2);
+      p->param.numMissVals = array_num_mv(n, odat, missval2);
     }
 
   return p;
@@ -309,7 +309,7 @@ expr_var_con(int init, int oper, const nodeType *p1, const nodeType *p2)
 {
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto hasMV = (p1->param.nmiss > 0);
+  auto hasMV = (p1->param.numMissVals > 0);
   auto datatype = p1->param.datatype;
   auto missval1 = p1->param.missval;
 
@@ -333,7 +333,7 @@ expr_var_con(int init, int oper, const nodeType *p1, const nodeType *p2)
 
       oper_expr_var_con(oper, hasMV, n, missval1, odat, idat, cval);
 
-      p->param.nmiss = array_num_mv(n, odat, missval1);
+      p->param.numMissVals = array_num_mv(n, odat, missval1);
     }
 
   return p;
@@ -343,8 +343,8 @@ static nodeType *
 expr_var_var(int init, int oper, nodeType *p1, nodeType *p2)
 {
   auto px = p1;
-  auto nmiss1 = p1->param.nmiss;
-  auto nmiss2 = p2->param.nmiss;
+  auto numMissVals1 = p1->param.numMissVals;
+  auto numMissVals2 = p2->param.numMissVals;
   auto missval1 = p1->param.missval;
   auto missval2 = p2->param.missval;
 
@@ -411,7 +411,7 @@ expr_var_var(int init, int oper, nodeType *p1, nodeType *p2)
     }
 
   p->param.name = tmpVarName;
-  // printf("%s %s nmiss %zu %zu\n", p->u.var.name.c_str(), px->param.name.c_str(), nmiss1, nmiss2);
+  // printf("%s %s numMissVals %zu %zu\n", p->u.var.name.c_str(), px->param.name.c_str(), numMissVals1, numMissVals2);
 
   if (!init)
     {
@@ -426,7 +426,7 @@ expr_var_var(int init, int oper, nodeType *p1, nodeType *p2)
           const auto idat1 = p1->param.data + loff1;
           const auto idat2 = p2->param.data + loff2;
           auto odat = p->param.data + loff;
-          auto hasMV = (nmiss1 > 0 || nmiss2 > 0);
+          auto hasMV = (numMissVals1 > 0 || numMissVals2 > 0);
 
           if (ngp1 != ngp2)
             {
@@ -447,7 +447,7 @@ expr_var_var(int init, int oper, nodeType *p1, nodeType *p2)
           else { oper_expr_var_var(oper, hasMV, ngp, missval1, missval2, odat, idat1, idat2); }
         }
 
-      p->param.nmiss = array_num_mv(ngp * nlev, p->param.data, missval1);
+      p->param.numMissVals = array_num_mv(ngp * nlev, p->param.data, missval1);
     }
 
   return p;
@@ -480,12 +480,12 @@ ex_copy_var(int init, nodeType *p2, const nodeType *p1)
 
   if (!init)
     {
-      if (copyConstValue1) { varray_fill(ngp2 * nlev2, p2->param.data, p1->param.data[0]); }
+      if (copyConstValue1) { ranges::fill_n(p2->param.data, ngp2 * nlev2, p1->param.data[0]); }
       else
         {
           array_copy(ngp2 * nlev2, p1->param.data, p2->param.data);
           p2->param.missval = p1->param.missval;
-          p2->param.nmiss = p1->param.nmiss;
+          p2->param.numMissVals = p1->param.numMissVals;
         }
     }
 }
@@ -518,7 +518,7 @@ ex_copy_con(int init, nodeType *p2, const nodeType *p1)
   if (!init)
     {
       assert(p2->param.data != nullptr);
-      varray_fill(ngp * nlev, p2->param.data, cval);
+      ranges::fill_n(p2->param.data, ngp * nlev, cval);
     }
 }
 
@@ -670,8 +670,7 @@ ex_fun_std(int funcID, size_t n, bool hasMV, double mv, const double *p1data, do
 }
 
 void
-func_expr_con_var(int funcID, bool hasMV, size_t n, double mv, double *odat, double cval,
-                  const double *idat)
+func_expr_con_var(int funcID, bool hasMV, size_t n, double mv, double *odat, double cval, const double *idat)
 {
   auto &funcEntry = funcSymbolTable[funcID];
   auto exprfunc = (double (*)(double, double)) funcEntry.func;
@@ -697,8 +696,7 @@ func_expr_con_var(int funcID, bool hasMV, size_t n, double mv, double *odat, dou
 }
 
 void
-func_expr_var_con(int funcID, bool hasMV, size_t n, double mv, double *odat, const double *idat,
-                  double cval)
+func_expr_var_con(int funcID, bool hasMV, size_t n, double mv, double *odat, const double *idat, double cval)
 {
   auto &funcEntry = funcSymbolTable[funcID];
   auto exprfunc = (double (*)(double, double)) funcEntry.func;
@@ -724,8 +722,7 @@ func_expr_var_con(int funcID, bool hasMV, size_t n, double mv, double *odat, con
 }
 
 void
-func_expr_var_var(int funcID, bool hasMV, size_t n, double mv1, double mv2, double *odat,
-                  const double *idat1, const double *idat2)
+func_expr_var_var(int funcID, bool hasMV, size_t n, double mv1, double mv2, double *odat, const double *idat1, const double *idat2)
 {
   auto &funcEntry = funcSymbolTable[funcID];
   auto exprfunc = (double (*)(double, double)) funcEntry.func;
@@ -762,7 +759,7 @@ ex_fun_var(int init, int funcID, nodeType *p1)
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
   auto nlat = p1->param.nlat;
-  auto nmiss = p1->param.nmiss;
+  auto numMissVals = p1->param.numMissVals;
   auto missval = p1->param.missval;
 
   auto p = new nodeType;
@@ -806,7 +803,7 @@ ex_fun_var(int init, int funcID, nodeType *p1)
       double *pdata = p->param.data;
       double *p1data = p1->param.data;
 
-      if (functype == FT_STD) { ex_fun_std(funcID, ngp * nlev, nmiss, missval, p1data, pdata); }
+      if (functype == FT_STD) { ex_fun_std(funcID, ngp * nlev, numMissVals, missval, p1data, pdata); }
       else if (functype == FT_FLD)
         {
           Field field;
@@ -820,7 +817,7 @@ ex_fun_var(int init, int funcID, nodeType *p1)
           auto exprfunc = (double (*)(const Field &)) funcEntry.func;
           for (size_t k = 0; k < nlev; ++k)
             {
-              fld_field_init(field, nmiss, missval, ngp, p1data + k * ngp, p1->param.weight);
+              fld_field_init(field, numMissVals, missval, ngp, p1data + k * ngp, p1->param.weight);
               pdata[k] = exprfunc(field);
             }
         }
@@ -832,9 +829,9 @@ ex_fun_var(int init, int funcID, nodeType *p1)
           auto exprfunc = (void (*)(const Field &, Field &)) funcEntry.func;
           for (size_t k = 0; k < nlev; ++k)
             {
-              fld_field_init(field1, nmiss, missval, ngp, &p1data[k * ngp], nullptr);
+              fld_field_init(field1, numMissVals, missval, ngp, &p1data[k * ngp], nullptr);
               field1.grid = gridID;
-              fld_field_init(field2, nmiss, missval, nlat, &pdata[k * nlat], nullptr);
+              fld_field_init(field2, numMissVals, missval, nlat, &pdata[k * nlat], nullptr);
               exprfunc(field1, field2);
               array_copy(nlat, field2.vec_d.data(), &pdata[k * nlat]);
             }
@@ -848,7 +845,7 @@ ex_fun_var(int init, int funcID, nodeType *p1)
           for (size_t i = 0; i < ngp; ++i)
             {
               for (size_t k = 0; k < nlev; ++k) field.vec_d[k] = p1data[k * ngp + i];
-              fld_field_init(field, nmiss, missval, nlev, nullptr, nullptr);
+              fld_field_init(field, numMissVals, missval, nlev, nullptr, nullptr);
               pdata[i] = exprfunc(field);
             }
         }
@@ -856,7 +853,7 @@ ex_fun_var(int init, int funcID, nodeType *p1)
       else
         cdo_abort("Intermal error, wrong function type (%d) for %s()!", functype, funcname);
 
-      if (pdata) p->param.nmiss = array_num_mv(p->param.ngp * p->param.nlev, pdata, missval);
+      if (pdata) p->param.numMissVals = array_num_mv(p->param.ngp * p->param.nlev, pdata, missval);
     }
 
   if (p1->isTmpObj) node_delete(p1);
@@ -894,7 +891,7 @@ func_con_var(int init, int funcID, const nodeType *p1, const nodeType *p2)
 
   auto ngp = (p2->param.ngp > 0) ? p2->param.ngp : 1;
   auto nlev = (p2->param.nlev > 0) ? p2->param.nlev : 1;
-  auto hasMV = (p2->param.nmiss > 0);
+  auto hasMV = (p2->param.numMissVals > 0);
   auto missval2 = p2->param.missval;
 
   auto n = ngp * nlev;
@@ -916,7 +913,7 @@ func_con_var(int init, int funcID, const nodeType *p1, const nodeType *p2)
 
       func_expr_con_var(funcID, hasMV, n, missval2, odat, cval, idat);
 
-      p->param.nmiss = array_num_mv(n, odat, missval2);
+      p->param.numMissVals = array_num_mv(n, odat, missval2);
     }
 
   return p;
@@ -931,7 +928,7 @@ func_var_con(int init, int funcID, const nodeType *p1, const nodeType *p2)
 
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto hasMV = (p1->param.nmiss > 0);
+  auto hasMV = (p1->param.numMissVals > 0);
   auto missval1 = p1->param.missval;
 
   auto n = ngp * nlev;
@@ -953,7 +950,7 @@ func_var_con(int init, int funcID, const nodeType *p1, const nodeType *p2)
 
       func_expr_var_con(funcID, hasMV, n, missval1, odat, idat, cval);
 
-      p->param.nmiss = array_num_mv(n, odat, missval1);
+      p->param.numMissVals = array_num_mv(n, odat, missval1);
     }
 
   return p;
@@ -967,8 +964,8 @@ func_var_var(int init, int funcID, nodeType *p1, nodeType *p2)
   if (functype != FT_STD) cdo_abort("Intermal error, wrong function type (%d) for %s()!", functype, funcEntry.name);
 
   auto px = p1;
-  auto nmiss1 = p1->param.nmiss;
-  auto nmiss2 = p2->param.nmiss;
+  auto numMissVals1 = p1->param.numMissVals;
+  auto numMissVals2 = p2->param.numMissVals;
   auto missval1 = p1->param.missval;
   auto missval2 = p2->param.missval;
 
@@ -1035,7 +1032,7 @@ func_var_var(int init, int funcID, nodeType *p1, nodeType *p2)
     }
 
   p->param.name = tmpVarName;
-  // printf("%s %s nmiss %zu %zu\n", p->u.var.name.c_str(), px->param.name.c_str(), nmiss1, nmiss2);
+  // printf("%s %s numMissVals %zu %zu\n", p->u.var.name.c_str(), px->param.name.c_str(), numMissVals1, numMissVals2);
 
   if (!init)
     {
@@ -1050,7 +1047,7 @@ func_var_var(int init, int funcID, nodeType *p1, nodeType *p2)
           const auto idat1 = p1->param.data + loff1;
           const auto idat2 = p2->param.data + loff2;
           auto odat = p->param.data + loff;
-          auto hasMV = (nmiss1 > 0 || nmiss2 > 0);
+          auto hasMV = (numMissVals1 > 0 || numMissVals2 > 0);
 
           if (ngp1 != ngp2)
             {
@@ -1071,7 +1068,7 @@ func_var_var(int init, int funcID, nodeType *p1, nodeType *p2)
           else { func_expr_var_var(funcID, hasMV, ngp, missval1, missval2, odat, idat1, idat2); }
         }
 
-      p->param.nmiss = array_num_mv(ngp * nlev, p->param.data, missval1);
+      p->param.numMissVals = array_num_mv(ngp * nlev, p->param.data, missval1);
     }
 
   return p;
@@ -1141,7 +1138,7 @@ ex_remap(int init, int funcID, nodeType *p1, nodeType *p2)
   auto gridID = p1->param.gridID;
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto nmiss = p1->param.nmiss;
+  auto numMissVals = p1->param.numMissVals;
   auto missval = p1->param.missval;
 
   auto p = new nodeType;
@@ -1173,10 +1170,10 @@ ex_remap(int init, int funcID, nodeType *p1, nodeType *p2)
           auto exprfunc = (void (*)(const Field &, Field &)) funcEntry.func;
           for (size_t k = 0; k < nlev; ++k)
             {
-              fld_field_init(field1, nmiss, missval, ngp, &p1data[k * ngp], nullptr);
+              fld_field_init(field1, numMissVals, missval, ngp, &p1data[k * ngp], nullptr);
               field1.grid = gridID;
               field2.grid = gridID2;
-              fld_field_init(field2, nmiss, missval, ngp2, &pdata[k * ngp2], nullptr);
+              fld_field_init(field2, numMissVals, missval, ngp2, &pdata[k * ngp2], nullptr);
               exprfunc(field1, field2);
               array_copy(ngp2, field2.vec_d.data(), &pdata[k * ngp2]);
             }
@@ -1253,7 +1250,7 @@ ex_fun1c(int init, int funcID, nodeType *p1, nodeType *p2, ParseParamType &parse
 
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto nmiss = p1->param.nmiss;
+  auto numMissVals = p1->param.numMissVals;
   auto missval = p1->param.missval;
 
   auto p = new nodeType;
@@ -1314,8 +1311,8 @@ ex_fun1c(int init, int funcID, nodeType *p1, nodeType *p2, ParseParamType &parse
       pdata = p->param.data;
       const auto p1data = p1->param.data + ngp * levidx;
       array_copy(ngp, p1data, pdata);
-      if (nmiss) nmiss = array_num_mv(ngp, pdata, missval);
-      p->param.nmiss = nmiss;
+      if (numMissVals) numMissVals = array_num_mv(ngp, pdata, missval);
+      p->param.numMissVals = numMissVals;
     }
 
   if (p1->isTmpObj) node_delete(p1);
@@ -1341,7 +1338,7 @@ ex_fun2c(int init, int funcID, nodeType *p1, nodeType *p2, nodeType *p3, ParsePa
 
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto nmiss = p1->param.nmiss;
+  auto numMissVals = p1->param.numMissVals;
   auto missval = p1->param.missval;
 
   auto p = new nodeType;
@@ -1420,8 +1417,8 @@ ex_fun2c(int init, int funcID, nodeType *p1, nodeType *p2, nodeType *p3, ParsePa
       auto paramdata = p->param.data;
       const auto p1data = p1->param.data + ngp * levidx1;
       array_copy(ngp * nlevout, p1data, paramdata);
-      if (nmiss) nmiss = array_num_mv(ngp * nlevout, paramdata, missval);
-      p->param.nmiss = nmiss;
+      if (numMissVals) numMissVals = array_num_mv(ngp * nlevout, paramdata, missval);
+      p->param.numMissVals = numMissVals;
     }
 
   if (p1->isTmpObj) node_delete(p1);
@@ -1476,7 +1473,7 @@ ex_uminus_var(int init, nodeType *p1)
 {
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto nmiss = p1->param.nmiss;
+  auto numMissVals = p1->param.numMissVals;
   auto missval = p1->param.missval;
 
   auto p = new nodeType;
@@ -1493,7 +1490,7 @@ ex_uminus_var(int init, nodeType *p1)
       double *pdata = p->param.data;
       const double *p1data = p1->param.data;
 
-      if (nmiss)
+      if (numMissVals)
         {
           for (size_t i = 0; i < ngp * nlev; ++i) pdata[i] = dbl_is_equal(p1data[i], missval) ? missval : -(p1data[i]);
         }
@@ -1502,7 +1499,7 @@ ex_uminus_var(int init, nodeType *p1)
           for (size_t i = 0; i < ngp * nlev; ++i) pdata[i] = -(p1data[i]);
         }
 
-      p->param.nmiss = nmiss;
+      p->param.numMissVals = numMissVals;
     }
 
   return p;
@@ -1549,7 +1546,7 @@ ex_not_var(int init, nodeType *p1)
 {
   auto ngp = (p1->param.ngp > 0) ? p1->param.ngp : 1;
   auto nlev = (p1->param.nlev > 0) ? p1->param.nlev : 1;
-  auto nmiss = p1->param.nmiss;
+  auto numMissVals = p1->param.numMissVals;
   auto missval = p1->param.missval;
 
   auto p = new nodeType;
@@ -1566,7 +1563,7 @@ ex_not_var(int init, nodeType *p1)
       double *pdata = p->param.data;
       const double *p1data = p1->param.data;
 
-      if (nmiss)
+      if (numMissVals)
         {
           for (size_t i = 0; i < ngp * nlev; ++i) pdata[i] = dbl_is_equal(p1data[i], missval) ? missval : unary_op_not(p1data[i]);
         }
@@ -1575,7 +1572,7 @@ ex_not_var(int init, nodeType *p1)
           for (size_t i = 0; i < ngp * nlev; ++i) pdata[i] = unary_op_not(p1data[i]);
         }
 
-      p->param.nmiss = nmiss;
+      p->param.numMissVals = numMissVals;
     }
 
   return p;
@@ -1621,14 +1618,8 @@ static void
 str_add_node_info(char *string, size_t stringlen, nodeType *p, const char *ext)
 {
   auto len = strlen(string);
-  if (p->type == NodeEnum::typeCon)
-    {
-      snprintf(string + len, stringlen - len, "%g%s", p->con().value, ext);
-    }
-  else
-    {
-      snprintf(string + len, stringlen - len, "%s[N%zu][L%zu]%s", p->var().name.c_str(), p->param.ngp, p->param.nlev, ext);
-    }
+  if (p->type == NodeEnum::typeCon) { snprintf(string + len, stringlen - len, "%g%s", p->con().value, ext); }
+  else { snprintf(string + len, stringlen - len, "%s[N%zu][L%zu]%s", p->var().name.c_str(), p->param.ngp, p->param.nlev, ext); }
 }
 
 static nodeType *
@@ -1660,19 +1651,16 @@ ex_ifelse(int init, nodeType *p1, nodeType *p2, nodeType *p3)
   auto nlev = (p0->param.nlev > 0) ? p0->param.nlev : 1;
   auto px = p0;
 
-  size_t nmiss1 = 0;
+  size_t numMissVals1 = 0;
   auto missval1 = missval;
   const double *pdata1 = nullptr;
   size_t ngp1 = 1;
   size_t nlev1 = 1;
 
-  if (p1->type == NodeEnum::typeCon)
-    {
-      pdata1 = &p1->con().value;
-    }
+  if (p1->type == NodeEnum::typeCon) { pdata1 = &p1->con().value; }
   else
     {
-      nmiss1 = p1->param.nmiss;
+      numMissVals1 = p1->param.numMissVals;
       ngp1 = (p1->param.ngp > 0) ? p1->param.ngp : 1;
       nlev1 = (p1->param.nlev > 0) ? p1->param.nlev : 1;
       missval1 = p1->param.missval;
@@ -1700,10 +1688,7 @@ ex_ifelse(int init, nodeType *p1, nodeType *p2, nodeType *p3)
   size_t ngp2 = 1;
   size_t nlev2 = 1;
 
-  if (p2->type == NodeEnum::typeCon)
-    {
-      pdata2 = &p2->con().value;
-    }
+  if (p2->type == NodeEnum::typeCon) { pdata2 = &p2->con().value; }
   else
     {
       ngp2 = (p2->param.ngp > 0) ? p2->param.ngp : 1;
@@ -1733,10 +1718,7 @@ ex_ifelse(int init, nodeType *p1, nodeType *p2, nodeType *p3)
   size_t ngp3 = 1;
   size_t nlev3 = 1;
 
-  if (p3->type == NodeEnum::typeCon)
-    {
-      pdata3 = &p3->con().value;
-    }
+  if (p3->type == NodeEnum::typeCon) { pdata3 = &p3->con().value; }
   else
     {
       ngp3 = (p3->param.ngp > 0) ? p3->param.ngp : 1;
@@ -1771,7 +1753,7 @@ ex_ifelse(int init, nodeType *p1, nodeType *p2, nodeType *p3)
 
   if (!init)
     {
-      size_t nmiss = 0;
+      size_t numMissVals = 0;
 
       p->param.data = new double[ngp * nlev];
 
@@ -1796,7 +1778,7 @@ ex_ifelse(int init, nodeType *p1, nodeType *p2, nodeType *p3)
               auto ival2 = idat2[(ngp2 > 1) ? i : 0];
               auto ival3 = idat3[(ngp3 > 1) ? i : 0];
 
-              if (nmiss1 && dbl_is_equal(ival1, missval1))
+              if (numMissVals1 && dbl_is_equal(ival1, missval1))
                 odat[i] = missval1;
               else if (is_not_equal(ival1, 0.0))
                 odat[i] = dbl_is_equal(ival2, missval2) ? missval1 : ival2;
@@ -1804,10 +1786,10 @@ ex_ifelse(int init, nodeType *p1, nodeType *p2, nodeType *p3)
                 odat[i] = dbl_is_equal(ival3, missval3) ? missval1 : ival3;
             }
 
-          nmiss += array_num_mv(ngp, odat, missval1);
+          numMissVals += array_num_mv(ngp, odat, missval1);
         }
 
-      p->param.nmiss = nmiss;
+      p->param.numMissVals = numMissVals;
     }
 
   if (p1->isTmpObj) node_delete(p1);
@@ -1895,7 +1877,7 @@ add_new_constant(const std::string &varname, ParseParamType &parseArg, std::vect
   params[varID].nlat = 0;
   params[varID].nlev = 0;
   params[varID].missval = -9.e33;
-  params[varID].nmiss = 0;
+  params[varID].numMissVals = 0;
   params[varID].name = varname;
   parseArg.nparams++;
   parseArg.cnparams++;
@@ -1911,7 +1893,7 @@ add_new_param(const std::string &varname, ParseParamType &parseArg, std::vector<
   params[varID].isValid = true;
   params[varID].hasMV = param.hasMV;
   params[varID].name = varname;
-  params[varID].nmiss = param.nmiss;
+  params[varID].numMissVals = param.numMissVals;
   if (param.units.size()) params[varID].units = param.units;
   if (param.longname.size()) params[varID].longname = param.longname;
   parseArg.nparams++;
@@ -1955,7 +1937,7 @@ expr_run_var_grid(const std::string &vnm, int coord, ParseParamType &parseArg)
 {
   auto &params = parseArg.params;
 
-  auto varname = vnm.substr(0, vnm.size() -2);
+  auto varname = vnm.substr(0, vnm.size() - 2);
   auto varID = param_search_name(parseArg.nparams, params, varname);
   if (varID == -1) cdo_abort("Coordinate %c: variable >%s< not found!", coord, varname);
 
@@ -1991,7 +1973,7 @@ expr_run_var_zaxis(const std::string &vnm, int coord, ParseParamType &parseArg)
 {
   auto &params = parseArg.params;
 
-  auto varname = vnm.substr(0, vnm.size() -2);
+  auto varname = vnm.substr(0, vnm.size() - 2);
   auto varID = param_search_name(parseArg.nparams, params, varname);
   if (varID == -1) cdo_abort("Coordinate %c: variable >%s< not found!", coord, varname);
 
@@ -2060,7 +2042,7 @@ expr_run_var(nodeType *p, ParseParamType &parseArg)
   if (!init)
     {
       p->param.data = params[varID].data;
-      p->param.nmiss = params[varID].nmiss;
+      p->param.numMissVals = params[varID].numMissVals;
     }
 
   if (parseArg.debug)
@@ -2227,7 +2209,7 @@ expr_run_opr(nodeType *p, ParseParamType &parseArg)
             p->isTmpObj = false;
 
             ex_copy(init, p, rnode);
-            params[varID].nmiss = p->param.nmiss;
+            params[varID].numMissVals = p->param.numMissVals;
           }
 
         if (rnode && rnode->isTmpObj)
diff --git a/src/expr.h b/src/expr.h
index 3f9b6c8c04e2c80aacff54b802af8f1f3192bb95..ce354ac123e977dc8ca78373c660e96aa63e4fbb 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -4,6 +4,8 @@
   Author: Uwe Schulzweida
 
 */
+#ifndef CDO_EXPR_H
+#define CDO_EXPR_H
 #include <cstdio>
 #include <cstdarg>
 #include <cassert>
@@ -26,8 +28,8 @@ enum CoordIndex
   MINUTE,
   HOUR,
   CALENDAR,
-  DOY,      // day of year
-  DPY,      // days per year
+  DOY,  // day of year
+  DPY,  // days per year
   LEN
 };
 
@@ -117,7 +119,7 @@ struct ParamEntry
   size_t ngp = 0;
   size_t nlat = 0;
   size_t nlev = 0;
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   std::string name;
   std::string longname;
   std::string stdname;
@@ -198,3 +200,4 @@ void yyset_extra(YY_EXTRA_TYPE, void *);
 
 nodeType *expr_run(nodeType *p, ParseParamType &parseArg);
 int params_get_coord_ID(const ParseParamType &parseArg, int coord, int cdiID);
+#endif
diff --git a/src/expr_fun.cc b/src/expr_fun.cc
index 3a10f0e6c7c71234998a31540852749f08e2aca0..22364ddca79a48499a86ab84a13ea2d79e4fcbd6 100644
--- a/src/expr_fun.cc
+++ b/src/expr_fun.cc
@@ -350,10 +350,10 @@ oper_expr_var_var(int oper, bool hasMV, size_t n, double mv1, double mv2, double
 }
 
 void
-fld_field_init(Field &field, size_t nmiss, double missval, size_t ngp, double *array, double *w)
+fld_field_init(Field &field, size_t numMissVals, double missval, size_t ngp, double *array, double *w)
 {
   field.size = ngp;
-  field.nmiss = nmiss;
+  field.numMissVals = numMissVals;
   field.missval = missval;
   if (array != nullptr) array_copy(ngp, array, field.vec_d.data());
   if (w != nullptr) array_copy(ngp, w, field.weightv.data());
@@ -384,8 +384,7 @@ void
 vert_weights(int zaxisID, size_t nlev, Varray<double> &weights)
 {
   Varray<double> thickness(nlev);
-  weights.resize(nlev);
-  varray_fill(nlev, weights, 1.0);
+  weights.resize(nlev, 1.0);
 
   if (nlev > 1)
     {
diff --git a/src/expr_fun.h b/src/expr_fun.h
index 509b4a268a8112df88f8444411890dbbad1a79c9..a0e2216100cb6be5b9bc78493ecc7d9076656ff3 100644
--- a/src/expr_fun.h
+++ b/src/expr_fun.h
@@ -9,13 +9,14 @@
 
 #include <cstddef>
 #include "field.h"
+#include "expr.h"
 
 nodeType *expr_con_con(int oper, const nodeType *p1, const nodeType *p2);
 void oper_expr_con_var(int oper, bool hasMV, size_t n, double mv, double *odat, double cval, const double *idat);
 void oper_expr_var_con(int oper, bool hasMV, size_t n, double mv, double *odat, const double *idat, double cval);
 void oper_expr_var_var(int oper, bool hasMV, size_t n, double mv1, double mv2, double *odat, const double *idat1, double *idat2);
 
-void fld_field_init(Field &field, size_t nmiss, double missval, size_t ngp, double *array, double *w);
+void fld_field_init(Field &field, size_t numMissVals, double missval, size_t ngp, double *array, double *w);
 void vert_weights(int zaxisID, size_t nlev, Varray<double> &weights);
 
 #endif
diff --git a/src/expr_yacc.cc b/src/expr_yacc.cc
index 986e9ec29b5b851d9cd44e2fd91d88de51e24256..14e3fec83385f4a189b054d8f4e93c5c1ac879b1 100644
--- a/src/expr_yacc.cc
+++ b/src/expr_yacc.cc
@@ -1507,7 +1507,7 @@ yyreduce:
 
   case 9: /* stmt: VARIABLE ';'  */
 #line 70 "expr_yacc.y"
-                                   { (yyval.nPtr) = expr_opr('=', 2, expr_var((yyvsp[-1].varnm)), expr_var((yyvsp[-1].varnm))); }
+                                   { char *v = strdup((yyvsp[-1].varnm)); (yyval.nPtr) = expr_opr('=', 2, expr_var((yyvsp[-1].varnm)), expr_var(v)); }
 #line 1512 "expr_yacc.cc"
     break;
 
diff --git a/src/expr_yacc.y b/src/expr_yacc.y
index 576dcd2f4382dba37ed17b4560a9f305c55356f4..207e9693af694b719008d1dcd8848fff96de5ace 100644
--- a/src/expr_yacc.y
+++ b/src/expr_yacc.y
@@ -67,7 +67,7 @@ stmt:
         | expr ';'                 { $$ = $1; }
         | VARIABLE '=' expr ';'    { $$ = expr_opr('=', 2, expr_var($1), $3); }
         | VARIABLE '=' ternary ';' { $$ = expr_opr('=', 2, expr_var($1), $3); }
-        | VARIABLE ';'             { $$ = expr_opr('=', 2, expr_var($1), expr_var($1)); } /* conflicts: 1 shift/reduce */
+        | VARIABLE ';'             { char *v = strdup($1); $$ = expr_opr('=', 2, expr_var($1), expr_var(v)); } /* conflicts: 1 shift/reduce */
         | REMOVE VARIABLE ')' ';'  { $$ = expr_cmd("remove", $2); }
         | PRINT VARIABLE ')' ';'   { $$ = expr_cmd("print", $2); }
         | '{' stmt_list '}'        { $$ = $2; }
diff --git a/src/factory.cc b/src/factory.cc
index aaf311fe5e4c3a7440c0a147e8b6dacf6c1b6ec7..ca4706ae070a8883da84005ffadb0d0fa5bc57f2 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -1,14 +1,232 @@
+/*
+  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
+
+  Author: Uwe Schulzweida
+          Oliver Heidmann
+
+*/
 #include <map>
 #include <string>
 #include <algorithm>
+#include <functional>
+
 #include "process.h"
+#include "factory.h"
+#include "util_string.h"
 
-std::map<const std::string, std::function<std::shared_ptr<Process>(int p_ID, const std::string &p_operatorNamme,
-                                                                   const std::vector<std::string> &operatorArguments)>> &
-get_factory()
+namespace Factory
 {
-  static std::map<const std::string, std::function<std::shared_ptr<Process>(int p_ID, const std::string &p_operatorNamme,
-                                                                            const std::vector<std::string> &operatorArguments)>>
-      factory;
+OperatorMap &
+get()
+{
+  static OperatorMap factory;
   return factory;
 }
+
+OperatorMap::iterator
+find(const std::string &p_operName)
+{
+  return find(p_operName, [&p_operName]() { cdo_abort("Operator >%s< not found!", p_operName); });
+}
+
+OperatorMap::iterator
+find(const std::string &p_operName, std::function<void()> p_onError)
+{
+  auto &operator_map = get();
+  auto it = operator_map.find(p_operName);
+  if (it == operator_map.end()) { p_onError(); }
+  return it;
+}
+
+const CdoModule &
+get_module(const std::string &p_operName)
+{
+  auto it = find(p_operName);
+  return it->second.module;
+}
+const CdoModule &
+get_module(const OperatorMap::iterator &it)
+{
+  return it->second.module;
+}
+
+ModuleConstructor
+get_constructor(const std::string &p_operName)
+{
+  auto it = find(p_operName);
+  return it->second.constructor;
+}
+
+ModuleConstructor
+get_constructor(const OperatorMap::iterator it)
+{
+  return it->second.constructor;
+}
+
+const CdoHelp &
+get_help(const std::string &p_operName)
+{
+  auto it = find(p_operName);
+  return get_help(it);
+};
+
+const CdoHelp &
+get_help(OperatorMap::iterator p_it)
+{
+  auto &mod = get_module(p_it);
+  auto &help_iter = mod.get_help(p_it->first);
+  return help_iter;
+}
+
+/**
+ * @param a pointer to a string/substring
+ * @param b pointer to a string/substring
+ * @param alen length of string a
+ * @param blen length of string b
+ * @retval true if a is similar to b
+ * @retval false if a is not similar to b
+ *
+ * Recursive function for finding substrings of a operator name that match other operators.
+ */
+static bool
+similar(const char *a, const char *b, unsigned long alen, unsigned long blen)
+{
+  if (alen > 2 && blen > 2 && strstr(b, a)) return true;
+
+  while (*a && *b && *a == *b)
+    {
+      a++;
+      b++;
+    }
+  if (!*a && !*b) return true;
+
+  //  printf("%d %d %s %s\n", alen, blen, a, b);
+
+  if (alen >= 2 && blen >= 1 && *a && similar(a + 1, b, alen - 2, blen - 1)) return true;
+
+  if (alen >= 1 && blen >= 2 && *b && similar(a, b + 1, alen - 1, blen - 2)) return true;
+
+  return false;
+}
+
+/**
+ * @param original string tested for similarity to \p other
+ * @param other string that \p original will be compared to
+ * @retval true if original and other are similar
+ * @retval false if not
+ *
+ * Wrapper function for #similar() to parse c++ strings to c strings
+ */
+static bool
+similar(const std::string &original, const std::string &other)
+{
+  return (similar(original.c_str(), other.c_str(), original.size(), other.size()));
+}
+
+/***
+ * function for finding similar operator names for the given string
+ * @param operatorName operator name to find similar operators for
+ * @returns A string with all found names. The string is seqmented into lines
+ * with a max length of 75 characters
+ */
+std::string
+find_similar_operators(const std::string &operatorName)
+{
+  std::string found_similar_operators = "";
+  size_t lines = 1;
+  constexpr size_t line_length = 105;
+
+  if (operatorName != "")
+    {
+      // Searching for similar operator names in operator to module map
+      for (const auto &str : Factory::get())
+        {
+          if (similar(string_to_lower(operatorName), str.first))
+            {
+              if (found_similar_operators.size() + str.first.size() > lines * line_length)
+                {
+                  found_similar_operators += "\n";
+                  lines++;
+                }
+              found_similar_operators += str.first;
+              found_similar_operators += " ";
+            }
+        }
+    }
+
+  if (found_similar_operators.size() == 0) { found_similar_operators = "(not found)"; }
+  return found_similar_operators;
+}
+
+/***
+ * Creates a sorted vector with all operator names and alisases excluding all modules that are marked as internal
+ * @return a sorted std::vector containing all operator names and aliases
+ * excluding all operators which modules are marked as internal
+ */
+std::vector<std::string>
+get_sorted_operator_name_list()
+{
+  std::vector<std::string> names;
+
+  auto &factory = Factory::get();
+
+  for (const auto &factory_entry : factory)
+    {
+      auto &module = factory_entry.second.module;
+      if (module.mode == 1) { names.push_back(factory_entry.first); }
+    }
+
+  std::ranges::sort(names);
+
+  return names;
+}
+
+std::string
+get_original(const std::string &operator_name)
+{
+  auto module = Factory::get_module(operator_name);
+  auto index = module.is_alias(operator_name);
+  if (index != -1) return module.aliases[index].original;
+  return operator_name;
+}
+
+/***
+ * Prints all operator names and their short descriptions
+ * Aliases are listed and point to their original operator name.
+ * If the module is not documented the description is empty
+ * If a module has only one operator the short module description is listed
+ * If the operator is not documented the description is empty
+ */
+
+OperatorMap::iterator
+find_module(const std::string &operator_name)
+{
+  return Factory::find(operator_name);
+}
+
+std::vector<std::string>
+get_module_operator_names(const std::string &module_name)
+{
+
+  std::vector<std::string> operator_names;
+
+  auto &modules = Factory::get();
+
+  std::string lower_name = "";
+  for (auto c : module_name) { lower_name += c; }
+
+  for (auto &registered_mod : modules)
+    {
+      const CdoModule &mod = registered_mod.second.module;
+
+      std::string lower_registered_name = "";
+      for (auto c : mod.name) { lower_registered_name += c; }
+
+      if (lower_registered_name == lower_name)
+        {
+          for (const oper_t &oper : mod.operators) { operator_names.push_back(oper.name); }
+        }
+    }
+  return operator_names;
+}
+};  // namespace Factory
diff --git a/src/factory.h b/src/factory.h
index 6f91629366e6bcbf92c530f118927c010f728012..7cf4a5375a25d464d46832a211ab5d644ec54a41 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -1,3 +1,10 @@
+/*
+  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
+
+  Author: Uwe Schulzweida
+          Oliver Heidmann
+
+*/
 #ifndef FACTORY_H
 #define FACTORY_H
 #include <vector>
@@ -5,58 +12,75 @@
 #include <map>
 #include <memory>
 #include <functional>
-#include "modules.h"
+#include <map>
+
+#include "cdo_module.h"
+#include "cdo_output.h"
 #include "process.h"
 
-std::map<const std::string, std::function<std::shared_ptr<Process>(int p_ID, const std::string &p_operatorNamme,
-                                                                   const std::vector<std::string> &operatorArguments)>> &
-get_factory();
+namespace Factory
+{
+// string -> pair(CdoModule,std::function)
+typedef std::function<std::shared_ptr<Process>(int, const std::string &, const std::vector<std::string> &)> ModuleConstructor;
 
-template <typename T>
-struct CdoModule
+struct FactoryEntry
 {
+  const CdoModule &module;
+  Factory::ModuleConstructor constructor;
+
+  FactoryEntry(const CdoModule &mod, Factory::ModuleConstructor con) : module(mod), constructor(con) {}
+};
+typedef std::map<std::string, FactoryEntry> OperatorMap;
+
+std::string find_similar_operators(const std::string &operatorName);
+
+std::vector<std::string> get_module_operator_names(const std::string &module_name);
+
+std::string get_original(const std::string &operatorName);
+
+std::vector<std::string> get_sorted_operator_name_list();
 
-  std::string name;
-  std::vector<oper_t> operators;  // Operator names
-  std::vector<Alias> aliases;
-  std::vector<char **> help;
-  short mode;    // Module mode: 0:intern 1:extern
-  short number;  // Allowed number type
-  module_constraints constraints;
+OperatorMap &get();  // Factory::get()
 
-  struct RegisterEntry
+OperatorMap::iterator find_module(const std::string &operatorName);
+OperatorMap::iterator find(const std::string &p_opername);
+OperatorMap::iterator find(const std::string &p_opername, std::function<void()> p_onError);
+
+const CdoModule &get_module(const std::string &p_operName);
+const CdoModule &get_module(const OperatorMap::iterator &it);
+
+ModuleConstructor get_constructor(const std::string &p_operName);
+ModuleConstructor get_constructor(const OperatorMap::iterator it);
+
+const CdoHelp &get_help(const std::string &p_operName);
+const CdoHelp &get_help(OperatorMap::iterator p_it);
+};  // namespace Factory
+
+template <typename T>
+struct RegisterEntry
+{
+  Factory::ModuleConstructor
+  create_constructor(const CdoModule &mod)
   {
-  public:
-    RegisterEntry(CdoModule *t)
-    {
-      for (auto &oper : t->operators)
-        {
-          get_factory()[oper.name] = [](int p_ID, const std::string &p_operatorNamme,
-                                        const std::vector<std::string> &operatorArguments) -> std::shared_ptr<T> {
-            auto new_mod = std::make_shared<T>();
-            new_mod->m_ID = p_ID;
-            new_mod->operatorName = p_operatorNamme;
-            new_mod->init_process(p_operatorNamme, operatorArguments);
-            /* START  Temporary workaround, replace by proper sys */
-            /* END  Temporary workaround, replace by proper sys */
-            return new_mod;
-          };
-          std::cerr << "added " << oper.name << std::endl;
-        }
-    };
-  };
+    return
+        [&mod](int p_ID, const std::string &p_operName, const std::vector<std::string> &p_operatorArguments) -> std::shared_ptr<T> {
 
-  int
-  get_id(std::string oper)
+          Debug(FACTORY,"Creating process via factory function, %d = ID, %s = name, %s = mod_name", p_ID, p_operName, mod.name);
+          auto new_process = std::make_shared<T>(p_ID, p_operName, p_operatorArguments, mod);
+          return new_process;
+        };
+  }
+  void
+  register_operator(const CdoModule &mod, std::string &p_oper_name)
   {
-    for (size_t i = 0; i < operators.size(); i++)
-      {
-        if (oper == operators[i].name) { return i; }
-      }
-    return -1;
+    Factory::get().insert(std::make_pair(p_oper_name, Factory::FactoryEntry(mod, create_constructor(mod))));
   }
 
-  RegisterEntry registration = RegisterEntry(this);
+public:
+  RegisterEntry(CdoModule &module)
+  {
+    for (auto &oper : module.operators) { register_operator(module, oper.name); }
+    for (auto &alias : module.aliases) { register_operator(module, alias.alias); }
+  };
 };
-
 #endif
diff --git a/src/field.cc b/src/field.cc
index 988431e2fc8a02ef19b60bd371794b1577400bfd..1e240fceb9ad23657766fa99e245f776cb00840a 100644
--- a/src/field.cc
+++ b/src/field.cc
@@ -23,7 +23,7 @@ Field::init(const CdoVar &var)
   nwpv = 1;
   grid = var.gridID;
   gridsize = var.gridsize;
-  nmiss = 0;
+  numMissVals = 0;
   missval = var.missval;
   memType = var.memType;
   size = var.gridsize * var.nwpv;
@@ -115,7 +115,7 @@ field_ncopy(size_t n, const Field &fieldIn, Field &fieldOut)
   if (n > fieldIn.size) cdo_abort("Source field to small (%s)", __func__);
   if (n > fieldOut.size) cdo_abort("Target field to small (%s)", __func__);
 
-  fieldOut.nmiss = fieldIn.nmiss;
+  fieldOut.numMissVals = fieldIn.numMissVals;
 
   // clang-format off
   if      (memtype_is_float_float  (fieldIn.memType, fieldOut.memType)) varray_copy(n, fieldIn.vec_f, fieldOut.vec_f);
@@ -131,7 +131,7 @@ field_copy(const Field &fieldIn, Field &fieldOut)
 {
   if (fieldIn.size > fieldOut.size) cdo_abort("Target field to small (%s)", __func__);
 
-  fieldOut.nmiss = fieldIn.nmiss;
+  fieldOut.numMissVals = fieldIn.numMissVals;
 
   if (fieldIn.memType == MemType::Float)
     fieldOut.vec_f = fieldIn.vec_f;
@@ -144,7 +144,7 @@ field_copy(const Field3D &fieldIn, Field3D &fieldOut)
 {
   if (fieldIn.size > fieldOut.size) cdo_abort("Target field to small (%s)", __func__);
 
-  fieldOut.nmiss = fieldIn.nmiss;
+  fieldOut.numMissVals = fieldIn.numMissVals;
 
   if (fieldIn.memType == MemType::Float)
     fieldOut.vec_f = fieldIn.vec_f;
@@ -220,75 +220,75 @@ size_t
 field_num_mv(Field &field)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = varray_num_mv(field.size, field.vec_f, (float) field.missval);
+    field.numMissVals = varray_num_mv(field.size, field.vec_f, (float) field.missval);
   else
-    field.nmiss = varray_num_mv(field.size, field.vec_d, field.missval);
+    field.numMissVals = varray_num_mv(field.size, field.vec_d, field.missval);
 
-  return field.nmiss;
+  return field.numMissVals;
 }
 
 MinMax
 field_min_max(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_min_max_mv(field.size, field.vec_f, (float) field.missval) : varray_min_max(field.vec_f);
+    return field.numMissVals ? varray_min_max_mv(field.size, field.vec_f, (float) field.missval) : varray_min_max(field.vec_f);
   else
-    return field.nmiss ? varray_min_max_mv(field.size, field.vec_d, field.missval) : varray_min_max(field.vec_d);
+    return field.numMissVals ? varray_min_max_mv(field.size, field.vec_d, field.missval) : varray_min_max(field.vec_d);
 }
 
 double
 field_min(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_min_mv(field.size, field.vec_f, (float) field.missval) : varray_min(field.size, field.vec_f);
+    return field.numMissVals ? varray_min_mv(field.size, field.vec_f, (float) field.missval) : varray_min(field.size, field.vec_f);
   else
-    return field.nmiss ? varray_min_mv(field.size, field.vec_d, field.missval) : varray_min(field.size, field.vec_d);
+    return field.numMissVals ? varray_min_mv(field.size, field.vec_d, field.missval) : varray_min(field.size, field.vec_d);
 }
 
 double
 field_max(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_max_mv(field.size, field.vec_f, (float) field.missval) : varray_max(field.size, field.vec_f);
+    return field.numMissVals ? varray_max_mv(field.size, field.vec_f, (float) field.missval) : varray_max(field.size, field.vec_f);
   else
-    return field.nmiss ? varray_max_mv(field.size, field.vec_d, field.missval) : varray_max(field.size, field.vec_d);
+    return field.numMissVals ? varray_max_mv(field.size, field.vec_d, field.missval) : varray_max(field.size, field.vec_d);
 }
 
 double
 field_range(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_range_mv(field.size, field.vec_f, (float) field.missval) : varray_range(field.size, field.vec_f);
+    return field.numMissVals ? varray_range_mv(field.size, field.vec_f, (float) field.missval) : varray_range(field.size, field.vec_f);
   else
-    return field.nmiss ? varray_range_mv(field.size, field.vec_d, field.missval) : varray_range(field.size, field.vec_d);
+    return field.numMissVals ? varray_range_mv(field.size, field.vec_d, field.missval) : varray_range(field.size, field.vec_d);
 }
 
 double
 field_sum(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_sum_mv(field.size, field.vec_f, (float) field.missval) : varray_sum(field.size, field.vec_f);
+    return field.numMissVals ? varray_sum_mv(field.size, field.vec_f, (float) field.missval) : varray_sum(field.size, field.vec_f);
   else
-    return field.nmiss ? varray_sum_mv(field.size, field.vec_d, field.missval) : varray_sum(field.size, field.vec_d);
+    return field.numMissVals ? varray_sum_mv(field.size, field.vec_d, field.missval) : varray_sum(field.size, field.vec_d);
 }
 
 double
 field_mean(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_mean_mv(field.size, field.vec_f, (float) field.missval) : varray_mean(field.size, field.vec_f);
+    return field.numMissVals ? varray_mean_mv(field.size, field.vec_f, (float) field.missval) : varray_mean(field.size, field.vec_f);
   else
-    return field.nmiss ? varray_mean_mv(field.size, field.vec_d, field.missval) : varray_mean(field.size, field.vec_d);
+    return field.numMissVals ? varray_mean_mv(field.size, field.vec_d, field.missval) : varray_mean(field.size, field.vec_d);
 }
 
 double
 field_meanw(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_weighted_mean_mv(field.size, field.vec_f, field.weightv, (float) field.missval)
+    return field.numMissVals ? varray_weighted_mean_mv(field.size, field.vec_f, field.weightv, (float) field.missval)
                        : varray_weighted_mean(field.size, field.vec_f, field.weightv, (float) field.missval);
   else
-    return field.nmiss ? varray_weighted_mean_mv(field.size, field.vec_d, field.weightv, field.missval)
+    return field.numMissVals ? varray_weighted_mean_mv(field.size, field.vec_d, field.weightv, field.missval)
                        : varray_weighted_mean(field.size, field.vec_d, field.weightv, field.missval);
 }
 
@@ -296,19 +296,19 @@ double
 field_avg(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_avg_mv(field.size, field.vec_f, (float) field.missval) : varray_mean(field.size, field.vec_f);
+    return field.numMissVals ? varray_avg_mv(field.size, field.vec_f, (float) field.missval) : varray_mean(field.size, field.vec_f);
   else
-    return field.nmiss ? varray_avg_mv(field.size, field.vec_d, field.missval) : varray_mean(field.size, field.vec_d);
+    return field.numMissVals ? varray_avg_mv(field.size, field.vec_d, field.missval) : varray_mean(field.size, field.vec_d);
 }
 
 double
 field_avgw(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.nmiss ? varray_weighted_avg_mv(field.size, field.vec_f, field.weightv, (float) field.missval)
+    return field.numMissVals ? varray_weighted_avg_mv(field.size, field.vec_f, field.weightv, (float) field.missval)
                        : varray_weighted_mean(field.size, field.vec_f, field.weightv, (float) field.missval);
   else
-    return field.nmiss ? varray_weighted_avg_mv(field.size, field.vec_d, field.weightv, field.missval)
+    return field.numMissVals ? varray_weighted_avg_mv(field.size, field.vec_d, field.weightv, field.missval)
                        : varray_weighted_mean(field.size, field.vec_d, field.weightv, field.missval);
 }
 
@@ -316,72 +316,72 @@ double
 field_var(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_var(field.size, field.vec_f, field.nmiss, (float) field.missval);
+    return varray_var(field.size, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    return varray_var(field.size, field.vec_d, field.nmiss, field.missval);
+    return varray_var(field.size, field.vec_d, field.numMissVals, field.missval);
 }
 
 double
 field_var1(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_var_1(field.size, field.vec_f, field.nmiss, (float) field.missval);
+    return varray_var_1(field.size, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    return varray_var_1(field.size, field.vec_d, field.nmiss, field.missval);
+    return varray_var_1(field.size, field.vec_d, field.numMissVals, field.missval);
 }
 
 double
 field_skew(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_skew(field.size, field.vec_f, field.nmiss, (float) field.missval);
+    return varray_skew(field.size, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    return varray_skew(field.size, field.vec_d, field.nmiss, field.missval);
+    return varray_skew(field.size, field.vec_d, field.numMissVals, field.missval);
 }
 
 double
 field_kurt(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_kurt(field.size, field.vec_f, field.nmiss, (float) field.missval);
+    return varray_kurt(field.size, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    return varray_kurt(field.size, field.vec_d, field.nmiss, field.missval);
+    return varray_kurt(field.size, field.vec_d, field.numMissVals, field.missval);
 }
 
 double
 field_median(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_median(field.size, field.vec_f, field.nmiss, (float) field.missval);
+    return varray_median(field.size, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    return varray_median(field.size, field.vec_d, field.nmiss, field.missval);
+    return varray_median(field.size, field.vec_d, field.numMissVals, field.missval);
 }
 
 double
 field_count(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_count(field.size, field.vec_f, field.nmiss, (float) field.missval);
+    return varray_count(field.size, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    return varray_count(field.size, field.vec_d, field.nmiss, field.missval);
+    return varray_count(field.size, field.vec_d, field.numMissVals, field.missval);
 }
 
 double
 field_varw(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_weighted_var(field.size, field.vec_f, field.weightv, field.nmiss, (float) field.missval);
+    return varray_weighted_var(field.size, field.vec_f, field.weightv, field.numMissVals, (float) field.missval);
   else
-    return varray_weighted_var(field.size, field.vec_d, field.weightv, field.nmiss, field.missval);
+    return varray_weighted_var(field.size, field.vec_d, field.weightv, field.numMissVals, field.missval);
 }
 
 double
 field_var1w(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return varray_weighted_var_1(field.size, field.vec_f, field.weightv, field.nmiss, (float) field.missval);
+    return varray_weighted_var_1(field.size, field.vec_f, field.weightv, field.numMissVals, (float) field.missval);
   else
-    return varray_weighted_var_1(field.size, field.vec_d, field.weightv, field.nmiss, field.missval);
+    return varray_weighted_var_1(field.size, field.vec_d, field.weightv, field.numMissVals, field.missval);
 }
 
 double
@@ -419,12 +419,12 @@ field_std1w(const Field &field)
 void
 field_rms(const Field &field, const Field &field2, Field &field3)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto grid1 = field.grid;
-  //  size_t nmiss1   = field.nmiss;
+  //  size_t numMissVals1   = field.numMissVals;
   const auto array1 = field.vec_d.data();
   auto grid2 = field2.grid;
-  //  size_t nmiss2   = field2.nmiss;
+  //  size_t numMissVals2   = field2.numMissVals;
   const auto array2 = field2.vec_d.data();
   auto missval1 = field.missval;
   auto missval2 = field2.missval;
@@ -435,7 +435,7 @@ field_rms(const Field &field, const Field &field2, Field &field3)
   auto len = gridInqSize(grid1);
   if (len != gridInqSize(grid2)) cdo_abort("fields have different size!");
 
-  // if ( nmiss1 )
+  // if ( numMissVals1 )
   {
     for (size_t i = 0; i < len; ++i)
       if (!is_EQ(w[i], missval1))
@@ -457,21 +457,21 @@ else
 
   auto ravg = SQRTM(DIVM(rsum, rsumw));
 
-  if (is_EQ(ravg, missval1)) rnmiss++;
+  if (is_EQ(ravg, missval1)) rnumMissVals++;
 
   field3.vec_d[0] = ravg;
-  field3.nmiss = rnmiss;
+  field3.numMissVals = rnumMissVals;
 }
 
 template <typename T>
 double
-array_pctl(size_t len, T *array, size_t nmiss, T missval, double pn)
+array_pctl(size_t len, T *array, size_t numMissVals, T missval, double pn)
 {
   double pctl = missval;
 
-  if (len != nmiss)
+  if (len != numMissVals)
     {
-      if (nmiss)
+      if (numMissVals)
         {
           Varray<T> v(len);
 
@@ -479,8 +479,8 @@ array_pctl(size_t len, T *array, size_t nmiss, T missval, double pn)
           for (size_t i = 0; i < len; ++i)
             if (!dbl_is_equal(array[i], missval)) v[j++] = array[i];
 
-          if (nmiss != len - j)
-            cdo_warning("Internal problem, inconsistent number of missing values (nmiss: exprected=%zu found=%zu!)", nmiss,
+          if (numMissVals != len - j)
+            cdo_warning("Internal problem, inconsistent number of missing values (numMissVals: exprected=%zu found=%zu!)", numMissVals,
                         len - j);
 
           pctl = percentile(v.data(), j, pn);
@@ -495,9 +495,9 @@ double
 field_pctl(Field &field, double pn)
 {
   if (field.memType == MemType::Float)
-    return array_pctl(field.size, field.vec_f.data(), field.nmiss, (float) field.missval, pn);
+    return array_pctl(field.size, field.vec_f.data(), field.numMissVals, (float) field.missval, pn);
   else
-    return array_pctl(field.size, field.vec_d.data(), field.nmiss, field.missval, pn);
+    return array_pctl(field.size, field.vec_d.data(), field.numMissVals, field.missval, pn);
 }
 
 static int
@@ -517,7 +517,7 @@ field_rank(Field &field)
   const auto array = &field.vec_d[1];
   auto len = field.size - 1;
 
-  if (field.nmiss) return field.missval;
+  if (field.numMissVals) return field.missval;
 
   std::qsort(array, len, sizeof(double), compare_double);
 
diff --git a/src/field.h b/src/field.h
index 0b6ab0b85e4da4966d532c7957a39da1de00f672..870a5b43c52f169f235ec0ea23946bb0209db650 100644
--- a/src/field.h
+++ b/src/field.h
@@ -47,7 +47,7 @@ public:
   size_t size = 0;
   size_t nsamp = 0;
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   double missval = 0;
 
   Varray<float> vec_f;
@@ -92,8 +92,17 @@ struct RecordInfo
 {
   int varID = 0;
   int levelID = 0;
-  void set(int _varID, int _levelID) { this->varID = _varID; this->levelID = _levelID; }
-  std::pair<int, int> get() const { return std::make_pair(varID, levelID); }
+  void
+  set(int _varID, int _levelID)
+  {
+    this->varID = _varID;
+    this->levelID = _levelID;
+  }
+  std::pair<int, int>
+  get() const
+  {
+    return std::make_pair(varID, levelID);
+  }
 };
 
 using FieldVector = std::vector<Field>;
diff --git a/src/field2.cc b/src/field2.cc
index 9bf4f885ad9e14ba613b9cc59d1568f8d80588bb..ee80cd65ed4b07ad70f1616c51e497757ac6f559 100644
--- a/src/field2.cc
+++ b/src/field2.cc
@@ -126,7 +126,7 @@ field2_vinit(Field &field1, const Field &field2)
   else
     varray2_arith_mv(field1.size, field1.vec_d, field2.vec_d, field1.missval, field2.missval, arith_vinit_mv);
 
-  field1.nmiss = field2.nmiss;
+  field1.numMissVals = field2.numMissVals;
 }
 
 // increment valid values
@@ -144,7 +144,7 @@ field2_vincr(Field &field1, const Field &field2)
   else
     varray2_arith_mv(field1.size, field1.vec_d, field2.vec_d, field1.missval, field2.missval, arith_vincr_mv);
 
-  field1.nmiss = field2.nmiss;
+  field1.numMissVals = field2.numMissVals;
 }
 
 // init valid values
@@ -169,7 +169,7 @@ field2_vinit(Field &field1, const Field &field2, int vinit)
   else
     field2_vinit(field1.size, field1.vec_d, field2.vec_d, field1.missval, vinit);
 
-  field1.nmiss = field2.nmiss;
+  field1.numMissVals = field2.numMissVals;
 }
 
 // increment valid values
@@ -195,7 +195,7 @@ field2_vincr(Field &field1, const Field &field2, int vincr)
   else
     field2_vincr(field1.size, field1.vec_d, field2.vec_d, field1.missval, vincr);
 
-  field1.nmiss = field2.nmiss;
+  field1.numMissVals = field2.numMissVals;
 }
 
 void
@@ -203,7 +203,7 @@ field2_add(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_add_mv);
@@ -234,7 +234,7 @@ field2_sum(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_sum_mv);
@@ -271,7 +271,7 @@ field2_sumw(Field &field1, const Field &field2, double w)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -285,7 +285,7 @@ field2_sumw(Field &field1, const Field &field2, double w)
               array1[i] = w * array2[i];
           }
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
   else
     {
@@ -314,7 +314,7 @@ field2_sumtr(Field &occur, const Field &field, double refval)
   auto len = occur.size;
   if (len != field.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (occur.nmiss || field.nmiss)
+  if (occur.numMissVals || field.numMissVals)
     {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -332,7 +332,7 @@ field2_sumtr(Field &occur, const Field &field, double refval)
             if (!dbl_is_equal(oarray[i], omissval)) oarray[i] = 0.0;
           }
 
-      occur.nmiss = field_num_miss(occur);
+      occur.numMissVals = field_num_miss(occur);
     }
   else
     {
@@ -348,7 +348,7 @@ field2_sumq(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_sumq_mv);
@@ -392,7 +392,7 @@ field2_sumqw(Field &field1, const Field &field2, double w)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -406,7 +406,7 @@ field2_sumqw(Field &field1, const Field &field2, double w)
               array1[i] = w * array2[i] * array2[i];
           }
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
   else
     {
@@ -422,7 +422,7 @@ field2_sub(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_sub_mv);
@@ -453,7 +453,7 @@ field2_mul(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_mul_mv);
@@ -484,7 +484,7 @@ field2_div(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_div_mv);
@@ -539,11 +539,11 @@ field2_set_miss(Field &field1, const Field &field2)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss)
+  if (field1.numMissVals)
     {
       for (size_t i = 0; i < len; ++i) array1[i] = dbl_is_equal(array1[i], missval1) ? array2[i] : array1[i];
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
 }
 
@@ -552,7 +552,7 @@ field2_min(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_min_mv);
@@ -583,7 +583,7 @@ field2_max(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_arith_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval, arith_max_mv);
@@ -623,14 +623,14 @@ auto field2_set_index = [](auto &v1, auto &v2, auto v3, auto idx) {
 
 template <typename T>
 void
-field2_minidx(size_t nmiss3, size_t len, double missval3, Field &field1, Field &field2, const Varray<T> &v3, int idx)
+field2_minidx(size_t numMissVals3, size_t len, double missval3, Field &field1, Field &field2, const Varray<T> &v3, int idx)
 {
   auto missval1 = field1.missval;
   auto missval2 = field2.missval;
   auto &v1 = field1.vec_d;
   auto &v2 = field2.vec_d;
 
-  if (field2.nmiss || nmiss3)
+  if (field2.numMissVals || numMissVals3)
     {
       for (size_t i = 0; i < len; ++i)
         {
@@ -660,21 +660,21 @@ field2_minidx(Field &field1, Field &field2, const Field &field3, int idx)
   if (field2.size != field3.size) cdo_abort("Fields have different size (%s)", __func__);
 
   if (field3.memType == MemType::Float)
-    field2_minidx(field3.nmiss, field3.size, field3.missval, field1, field2, field3.vec_f, idx);
+    field2_minidx(field3.numMissVals, field3.size, field3.missval, field1, field2, field3.vec_f, idx);
   else
-    field2_minidx(field3.nmiss, field3.size, field3.missval, field1, field2, field3.vec_d, idx);
+    field2_minidx(field3.numMissVals, field3.size, field3.missval, field1, field2, field3.vec_d, idx);
 }
 
 template <typename T>
 void
-field2_maxidx(size_t nmiss3, size_t len, double missval3, Field &field1, Field &field2, const Varray<T> &v3, int idx)
+field2_maxidx(size_t numMissVals3, size_t len, double missval3, Field &field1, Field &field2, const Varray<T> &v3, int idx)
 {
   auto missval1 = field1.missval;
   auto missval2 = field2.missval;
   auto &v1 = field1.vec_d;
   auto &v2 = field2.vec_d;
 
-  if (field2.nmiss || nmiss3)
+  if (field2.numMissVals || numMissVals3)
     {
       for (size_t i = 0; i < len; ++i)
         {
@@ -704,15 +704,15 @@ field2_maxidx(Field &field1, Field &field2, const Field &field3, int idx)
   if (field2.size != field3.size) cdo_abort("Fields have different size (%s)", __func__);
 
   if (field3.memType == MemType::Float)
-    field2_maxidx(field3.nmiss, field3.size, field3.missval, field1, field2, field3.vec_f, idx);
+    field2_maxidx(field3.numMissVals, field3.size, field3.missval, field1, field2, field3.vec_f, idx);
   else
-    field2_maxidx(field3.nmiss, field3.size, field3.missval, field1, field2, field3.vec_d, idx);
+    field2_maxidx(field3.numMissVals, field3.size, field3.missval, field1, field2, field3.vec_d, idx);
 }
 
 static size_t
-field_set_nmiss(size_t len, Varray<double> &v, double missval)
+field_set_numMissVals(size_t len, Varray<double> &v, double missval)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
 
   if (std::isnan(missval))
     {
@@ -720,7 +720,7 @@ field_set_nmiss(size_t len, Varray<double> &v, double missval)
         if (dbl_is_equal(v[i], missval) || v[i] < 0)
           {
             v[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
   else
@@ -729,11 +729,11 @@ field_set_nmiss(size_t len, Varray<double> &v, double missval)
         if (is_equal(v[i], missval) || v[i] < 0)
           {
             v[i] = missval;
-            nmiss++;
+            numMissVals++;
           }
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 void
@@ -748,7 +748,7 @@ field2_var(Field &field1, const Field &field2, const Field &field3, int divisor)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       auto is_EQ = dbl_is_equal;
       for (size_t i = 0; i < len; ++i)
@@ -768,7 +768,7 @@ field2_var(Field &field1, const Field &field2, const Field &field3, int divisor)
         }
     }
 
-  field1.nmiss = field_set_nmiss(len, array1, missval1);
+  field1.numMissVals = field_set_numMissVals(len, array1, missval1);
 }
 
 void
@@ -782,15 +782,15 @@ field2_std(Field &field1, const Field &field2, const Field &field3, int divisor)
 
   field2_var(field1, field2, field3, divisor);
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < len; ++i)
     if (dbl_is_equal(array1[i], missval1) || array1[i] < 0)
       {
         array1[i] = missval1;
-        nmiss++;
+        numMissVals++;
       }
     else { array1[i] = is_not_equal(array1[i], 0) ? std::sqrt(array1[i]) : 0; }
-  field1.nmiss = nmiss;
+  field1.numMissVals = numMissVals;
 }
 
 void
@@ -809,7 +809,7 @@ fieldc_var(Field &field1, const Field &field2, int numSets, int divisor)
     {
       for (size_t i = 0; i < len; ++i) array1[i] = missval1;
     }
-  else if (field1.nmiss || field2.nmiss)
+  else if (field1.numMissVals || field2.numMissVals)
     {
       auto is_EQ = dbl_is_equal;
       for (size_t i = 0; i < len; ++i)
@@ -829,7 +829,7 @@ fieldc_var(Field &field1, const Field &field2, int numSets, int divisor)
         }
     }
 
-  field1.nmiss = field_set_nmiss(len, array1, missval1);
+  field1.numMissVals = field_set_numMissVals(len, array1, missval1);
 }
 
 void
@@ -843,16 +843,16 @@ fieldc_std(Field &field1, const Field &field2, int numSets, int divisor)
 
   fieldc_var(field1, field2, numSets, divisor);
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i = 0; i < len; ++i)
     if (dbl_is_equal(array1[i], missval1) || array1[i] < 0)
       {
         array1[i] = missval1;
-        nmiss++;
+        numMissVals++;
       }
     else { array1[i] = is_not_equal(array1[i], 0) ? std::sqrt(array1[i]) : 0; }
 
-  field1.nmiss = nmiss;
+  field1.numMissVals = numMissVals;
 }
 
 void
@@ -866,7 +866,7 @@ field2_moq(Field &field1, const Field &field2)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field2.nmiss)
+  if (field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (!dbl_is_equal(array2[i], missval2))
@@ -874,7 +874,7 @@ field2_moq(Field &field1, const Field &field2)
         else
           array1[i] = missval1;
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
   else
     {
@@ -893,7 +893,7 @@ field2_moqw(Field &field1, const Field &field2, double w)
   auto len = field1.size;
   if (len != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field2.nmiss)
+  if (field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (!dbl_is_equal(array2[i], missval2))
@@ -901,7 +901,7 @@ field2_moqw(Field &field1, const Field &field2, double w)
         else
           array1[i] = missval1;
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
   else
     {
@@ -910,7 +910,7 @@ field2_moqw(Field &field1, const Field &field2, double w)
 }
 
 /**
- * Counts the number of nonmissing values. The result of the operation is computed according to the following rules:
+ * Counts the number of nonumMissValsing values. The result of the operation is computed according to the following rules:
  *
  * field1  field2  result
  * a       b       a + 1
@@ -955,7 +955,7 @@ field2_count(Field &field1, const Field &field2)
 {
   if (field1.size != field2.size) cdo_abort("Fields have different size (%s)", __func__);
 
-  if (field1.nmiss || field2.nmiss)
+  if (field1.numMissVals || field2.numMissVals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         varray2_count_mv(field1.size, field1.vec_f, field2.vec_f, field1.missval, field2.missval);
@@ -966,7 +966,7 @@ field2_count(Field &field1, const Field &field2)
       else
         varray2_count_mv(field1.size, field1.vec_d, field2.vec_d, field1.missval, field2.missval);
 
-      field1.nmiss = field_num_miss(field1);
+      field1.numMissVals = field_num_miss(field1);
     }
   else
     {
diff --git a/src/field_meridional.cc b/src/field_meridional.cc
index 6b40579662a57428ecd69bf47964433aac053383..afac0b9dee7539b5922a967216012d289b09f695 100644
--- a/src/field_meridional.cc
+++ b/src/field_meridional.cc
@@ -28,8 +28,8 @@ varray_copy_meridional(size_t i, size_t nx, size_t ny, const Varray<T> &v1, Varr
 static void
 meridional_kernel_1(const Field &field1, Field &field2, funcType1 func, funcTypeMV1 funcMV)
 {
-  size_t rnmiss = 0;
-  auto nmiss = field1.nmiss;
+  size_t rnumMissVals = 0;
+  auto numMissVals = field1.numMissVals;
   auto missval = field1.missval;
   auto nx = gridInqXsize(field1.grid);
   auto ny = gridInqYsize(field1.grid);
@@ -43,19 +43,19 @@ meridional_kernel_1(const Field &field1, Field &field2, funcType1 func, funcType
       else
         varray_copy_meridional(i, nx, ny, field1.vec_d, v);
 
-      auto result = nmiss ? funcMV(ny, v, missval) : func(ny, v);
-      if (dbl_is_equal(result, missval)) rnmiss++;
+      auto result = numMissVals ? funcMV(ny, v, missval) : func(ny, v);
+      if (dbl_is_equal(result, missval)) rnumMissVals++;
       field2.vec_d[i] = result;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
 meridional_kernel_2(const Field &field1, Field &field2, funcType2 func, funcTypeMV2 funcMV)
 {
-  size_t rnmiss = 0;
-  auto nmiss = field1.nmiss;
+  size_t rnumMissVals = 0;
+  auto numMissVals = field1.numMissVals;
   auto missval = field1.missval;
   auto nx = gridInqXsize(field1.grid);
   auto ny = gridInqYsize(field1.grid);
@@ -70,19 +70,19 @@ meridional_kernel_2(const Field &field1, Field &field2, funcType2 func, funcType
       else
         varray_copy_meridional(i, nx, ny, field1.vec_d, v);
 
-      auto result = nmiss ? funcMV(ny, v, w, missval) : func(ny, v, w, missval);
-      if (dbl_is_equal(result, missval)) rnmiss++;
+      auto result = numMissVals ? funcMV(ny, v, w, missval) : func(ny, v, w, missval);
+      if (dbl_is_equal(result, missval)) rnumMissVals++;
       field2.vec_d[i] = result;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
 meridional_kernel_3(const Field &field1, Field &field2, funcType3 func)
 {
-  size_t rnmiss = 0;
-  auto nmiss = field1.nmiss;
+  size_t rnumMissVals = 0;
+  auto numMissVals = field1.numMissVals;
   auto missval = field1.missval;
   auto nx = gridInqXsize(field1.grid);
   auto ny = gridInqYsize(field1.grid);
@@ -97,19 +97,19 @@ meridional_kernel_3(const Field &field1, Field &field2, funcType3 func)
       else
         varray_copy_meridional(i, nx, ny, field1.vec_d, v);
 
-      auto result = func(ny, v, w, nmiss, missval);
-      if (dbl_is_equal(result, missval)) rnmiss++;
+      auto result = func(ny, v, w, numMissVals, missval);
+      if (dbl_is_equal(result, missval)) rnumMissVals++;
       field2.vec_d[i] = result;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
 meridional_kernel_4(const Field &field1, Field &field2, funcType4 func)
 {
-  size_t rnmiss = 0;
-  auto nmiss = field1.nmiss;
+  size_t rnumMissVals = 0;
+  auto numMissVals = field1.numMissVals;
   auto missval = field1.missval;
   auto nx = gridInqXsize(field1.grid);
   auto ny = gridInqYsize(field1.grid);
@@ -123,13 +123,13 @@ meridional_kernel_4(const Field &field1, Field &field2, funcType4 func)
       else
         varray_copy_meridional(i, nx, ny, field1.vec_d, v);
 
-      auto numMissval = ny - varray_count(ny, v, nmiss, missval);
+      auto numMissval = ny - varray_count(ny, v, numMissVals, missval);
       auto result = func(ny, v, numMissval, missval);
-      if (dbl_is_equal(result, missval)) rnmiss++;
+      if (dbl_is_equal(result, missval)) rnumMissVals++;
       field2.vec_d[i] = result;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
@@ -183,7 +183,7 @@ meridional_var1w(const Field &field1, Field &field2)
 static void
 meridional_stdw(const Field &field1, Field &field2)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto missval = field1.missval;
 
   auto nx = gridInqXsize(field1.grid);
@@ -193,17 +193,17 @@ meridional_stdw(const Field &field1, Field &field2)
   for (size_t i = 0; i < nx; ++i)
     {
       auto rstd = var_to_std(field2.vec_d[i], missval);
-      if (dbl_is_equal(rstd, missval)) rnmiss++;
+      if (dbl_is_equal(rstd, missval)) rnumMissVals++;
       field2.vec_d[i] = rstd;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
 meridional_std1w(const Field &field1, Field &field2)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto missval = field1.missval;
 
   auto nx = gridInqXsize(field1.grid);
@@ -213,11 +213,11 @@ meridional_std1w(const Field &field1, Field &field2)
   for (size_t i = 0; i < nx; ++i)
     {
       auto rstd = var_to_std(field2.vec_d[i], missval);
-      if (dbl_is_equal(rstd, missval)) rnmiss++;
+      if (dbl_is_equal(rstd, missval)) rnumMissVals++;
       field2.vec_d[i] = rstd;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
@@ -241,7 +241,7 @@ meridional_median(const Field &field1, Field &field2)
 void
 meridional_pctl(const Field &field1, Field &field2, double pn)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto missval = field1.missval;
 
   auto nx = gridInqXsize(field1.grid);
@@ -249,7 +249,7 @@ meridional_pctl(const Field &field1, Field &field2, double pn)
 
   Varray<double> v(ny);
 
-  if (field1.nmiss)
+  if (field1.numMissVals)
     {
       for (size_t i = 0; i < nx; ++i)
         {
@@ -269,7 +269,7 @@ meridional_pctl(const Field &field1, Field &field2, double pn)
           else
             {
               field2.vec_d[i] = missval;
-              rnmiss++;
+              rnumMissVals++;
             }
         }
     }
@@ -289,12 +289,12 @@ meridional_pctl(const Field &field1, Field &field2, double pn)
           else
             {
               field2.vec_d[i] = missval;
-              rnmiss++;
+              rnumMissVals++;
             }
         }
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 void
diff --git a/src/field_vinterp.cc b/src/field_vinterp.cc
index d5e0d529e7c779cf5a2598c2dc5dee40d383db4a..f46b63847f783aa9da178395fb11a525e994d2b1 100644
--- a/src/field_vinterp.cc
+++ b/src/field_vinterp.cc
@@ -19,14 +19,14 @@ gen_vert_index(std::vector<int> &vertIndex, Varray<double> &plev, Field3D &full_
 }
 
 void
-gen_vert_index_mv(std::vector<int> &vertIndex, Varray<double> &plev, size_t gridsize, Field &level0, Varray<size_t> &pnmiss,
+gen_vert_index_mv(std::vector<int> &vertIndex, Varray<double> &plev, size_t gridsize, Field &level0, Varray<size_t> &pnumMissVals,
                   bool lreverse)
 {
   auto nplev = plev.size();
   if (level0.memType == MemType::Float)
-    gen_vert_index_mv(vertIndex.data(), plev.data(), gridsize, nplev, level0.vec_f.data(), pnmiss.data(), lreverse);
+    gen_vert_index_mv(vertIndex.data(), plev.data(), gridsize, nplev, level0.vec_f.data(), pnumMissVals.data(), lreverse);
   else
-    gen_vert_index_mv(vertIndex.data(), plev.data(), gridsize, nplev, level0.vec_d.data(), pnmiss.data(), lreverse);
+    gen_vert_index_mv(vertIndex.data(), plev.data(), gridsize, nplev, level0.vec_d.data(), pnumMissVals.data(), lreverse);
 }
 
 void
diff --git a/src/field_vinterp.h b/src/field_vinterp.h
index 8e9ff78e3c8b9e2a911fb316166a3bc730ea12fa..7c0416b3477443ba07ca0f5563c04740c65664da 100644
--- a/src/field_vinterp.h
+++ b/src/field_vinterp.h
@@ -12,7 +12,7 @@
 
 void gen_vert_index(std::vector<int> &vertIndex, Varray<double> &plev, Field3D &full_level, size_t gridsize, bool lreverse = false);
 
-void gen_vert_index_mv(std::vector<int> &vertIndex, Varray<double> &plev, size_t gridsize, Field &level0, Varray<size_t> &pnmiss,
+void gen_vert_index_mv(std::vector<int> &vertIndex, Varray<double> &plev, size_t gridsize, Field &level0, Varray<size_t> &pnumMissVals,
                        bool lreverse = false);
 
 void vertical_interp_T(size_t nlevels, Field3D &full_level, Field3D &half_level, Field3D &field1, Field3D &field2, Field &sgeopot,
diff --git a/src/field_zonal.cc b/src/field_zonal.cc
index 7c0db28becf58c839f443482e700738fb3f2f198..7ba5239a7cf01156c15ba4e1bc3e8b421b93630a 100644
--- a/src/field_zonal.cc
+++ b/src/field_zonal.cc
@@ -51,8 +51,8 @@ using funcTypeNmissMV = double(size_t, const Varray<double> &, size_t, double);
 static void
 zonal_kernel_1(const Field &field1, Field &field2, funcTypeNmissMV funcNmissMV)
 {
-  size_t rnmiss = 0;
-  auto nmiss = field1.nmiss;
+  size_t rnumMissVals = 0;
+  auto numMissVals = field1.numMissVals;
   auto missval = field1.missval;
 
   auto ny = gridInqYsize(field1.grid);
@@ -68,20 +68,20 @@ zonal_kernel_1(const Field &field1, Field &field2, funcTypeNmissMV funcNmissMV)
       size_t offset = isReducedGrid ? cumreducedPoints[j] : j * nx;
       copy_latitude_row(offset, nx, field1, v);
 
-      auto numMissval = nx - varray_count(nx, v, nmiss, missval);
+      auto numMissval = nx - varray_count(nx, v, numMissVals, missval);
       auto result = funcNmissMV(nx, v, numMissval, missval);
-      if (dbl_is_equal(result, missval)) rnmiss++;
+      if (dbl_is_equal(result, missval)) rnumMissVals++;
       field2.vec_d[j] = result;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 static void
 zonal_kernel_2(const Field &field1, Field &field2, funcType func, funcTypeMV funcMV)
 {
-  size_t rnmiss = 0;
-  auto nmiss = field1.nmiss;
+  size_t rnumMissVals = 0;
+  auto numMissVals = field1.numMissVals;
   auto missval = field1.missval;
 
   auto ny = gridInqYsize(field1.grid);
@@ -97,12 +97,12 @@ zonal_kernel_2(const Field &field1, Field &field2, funcType func, funcTypeMV fun
       size_t offset = isReducedGrid ? cumreducedPoints[j] : j * nx;
       copy_latitude_row(offset, nx, field1, v);
 
-      auto result = nmiss ? funcMV(nx, v, missval) : func(nx, v);
-      if (dbl_is_equal(result, missval)) rnmiss++;
+      auto result = numMissVals ? funcMV(nx, v, missval) : func(nx, v);
+      if (dbl_is_equal(result, missval)) rnumMissVals++;
       field2.vec_d[j] = result;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 void
@@ -156,7 +156,7 @@ zonal_var1(const Field &field1, Field &field2)
 void
 zonal_std(const Field &field1, Field &field2)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto missval = field2.missval;
   auto ny = gridInqYsize(field2.grid);
 
@@ -165,17 +165,17 @@ zonal_std(const Field &field1, Field &field2)
   for (size_t j = 0; j < ny; ++j)
     {
       auto rstd = var_to_std(field2.vec_d[j], missval);
-      if (dbl_is_equal(rstd, missval)) rnmiss++;
+      if (dbl_is_equal(rstd, missval)) rnumMissVals++;
       field2.vec_d[j] = rstd;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 void
 zonal_std1(const Field &field1, Field &field2)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto missval = field2.missval;
   auto ny = gridInqYsize(field2.grid);
 
@@ -184,11 +184,11 @@ zonal_std1(const Field &field1, Field &field2)
   for (size_t j = 0; j < ny; ++j)
     {
       auto rstd = var_to_std(field2.vec_d[j], missval);
-      if (dbl_is_equal(rstd, missval)) rnmiss++;
+      if (dbl_is_equal(rstd, missval)) rnumMissVals++;
       field2.vec_d[j] = rstd;
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 void
@@ -212,7 +212,7 @@ zonal_median(const Field &field1, Field &field2)
 void
 zonal_pctl(const Field &field1, Field &field2, double pn)
 {
-  size_t rnmiss = 0;
+  size_t rnumMissVals = 0;
   auto missval = field2.missval;
 
   auto ny = gridInqYsize(field1.grid);
@@ -222,7 +222,7 @@ zonal_pctl(const Field &field1, Field &field2, double pn)
 
   Varray<double> v(nx);
 
-  if (field1.nmiss)
+  if (field1.numMissVals)
     {
       for (size_t j = 0; j < ny; ++j)
         {
@@ -238,7 +238,7 @@ zonal_pctl(const Field &field1, Field &field2, double pn)
           else
             {
               field2.vec_d[j] = missval;
-              rnmiss++;
+              rnumMissVals++;
             }
         }
     }
@@ -254,12 +254,12 @@ zonal_pctl(const Field &field1, Field &field2, double pn)
           else
             {
               field2.vec_d[j] = missval;
-              rnmiss++;
+              rnumMissVals++;
             }
         }
     }
 
-  field2.nmiss = rnmiss;
+  field2.numMissVals = rnumMissVals;
 }
 
 void
diff --git a/src/fieldc.cc b/src/fieldc.cc
index e6735a82113ef83e61eee400d5364a9bb86ae743..eece907208488ba479bc05f0a8e64877052d1499 100644
--- a/src/fieldc.cc
+++ b/src/fieldc.cc
@@ -11,13 +11,13 @@
 
 template <typename T>
 static void
-fieldc_mul(size_t len, size_t nmiss, Varray<T> &v, T missval, double rconst)
+fieldc_mul(size_t len, size_t numMissVals, Varray<T> &v, T missval, double rconst)
 {
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
   auto missval2 = missval;
 
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i) v[i] = MULM(v[i], rconst);
     }
@@ -31,51 +31,51 @@ void
 fieldc_mul(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    fieldc_mul(field.size, field.nmiss, field.vec_f, (float) field.missval, rconst);
+    fieldc_mul(field.size, field.numMissVals, field.vec_f, (float) field.missval, rconst);
   else
-    fieldc_mul(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_mul(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static size_t
-fieldc_div(size_t len, size_t nmiss, Varray<T> &v, T missval, double rconst)
+fieldc_div(size_t len, size_t numMissVals, Varray<T> &v, T missval, double rconst)
 {
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
   auto missval2 = missval;
 
-  if (nmiss || is_equal(rconst, 0))
+  if (numMissVals || is_equal(rconst, 0))
     {
       for (size_t i = 0; i < len; ++i) v[i] = DIVM(v[i], rconst);
 
-      if (is_equal(rconst, 0)) nmiss = len;
+      if (is_equal(rconst, 0)) numMissVals = len;
     }
   else
     {
       for (size_t i = 0; i < len; ++i) v[i] /= rconst;
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 void
 fieldc_div(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    field.nmiss = fieldc_div(field.size, field.nmiss, field.vec_f, (float) field.missval, rconst);
+    field.numMissVals = fieldc_div(field.size, field.numMissVals, field.vec_f, (float) field.missval, rconst);
   else
-    field.nmiss = fieldc_div(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    field.numMissVals = fieldc_div(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static void
-fieldc_add(size_t len, size_t nmiss, Varray<T> &v, T missval, double rconst)
+fieldc_add(size_t len, size_t numMissVals, Varray<T> &v, T missval, double rconst)
 {
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
   auto missval2 = missval;
 
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i) v[i] = ADDM(v[i], rconst);
     }
@@ -89,9 +89,9 @@ void
 fieldc_add(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    fieldc_add(field.size, field.nmiss, field.vec_f, (float) field.missval, rconst);
+    fieldc_add(field.size, field.numMissVals, field.vec_f, (float) field.missval, rconst);
   else
-    fieldc_add(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_add(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 void
@@ -102,12 +102,12 @@ fieldc_sub(Field &field, const double rconst)
 
 template <typename T>
 static void
-fieldc_min(size_t len, size_t nmiss, Varray<T> &v, T missval, T rconst)
+fieldc_min(size_t len, size_t numMissVals, Varray<T> &v, T missval, T rconst)
 {
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
 
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (is_EQ(v[i], missval1) || v[i] > rconst) v[i] = rconst;
@@ -123,19 +123,19 @@ void
 fieldc_min(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    fieldc_min(field.size, field.nmiss, field.vec_f, (float) field.missval, (float) rconst);
+    fieldc_min(field.size, field.numMissVals, field.vec_f, (float) field.missval, (float) rconst);
   else
-    fieldc_min(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_min(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static void
-fieldc_max(size_t len, size_t nmiss, Varray<T> &v, T missval, T rconst)
+fieldc_max(size_t len, size_t numMissVals, Varray<T> &v, T missval, T rconst)
 {
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
 
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
         if (is_EQ(v[i], missval1) || v[i] < rconst) v[i] = rconst;
@@ -151,17 +151,17 @@ void
 fieldc_max(Field &field, double rconst)
 {
   if (field.memType == MemType::Float)
-    fieldc_max(field.size, field.nmiss, field.vec_f, (float) field.missval, (float) rconst);
+    fieldc_max(field.size, field.numMissVals, field.vec_f, (float) field.missval, (float) rconst);
   else
-    fieldc_max(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_max(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static void
-fieldc_mod(size_t len, size_t nmiss, Varray<T> &v, T missval, double divisor)
+fieldc_mod(size_t len, size_t numMissVals, Varray<T> &v, T missval, double divisor)
 {
-  (void) nmiss;
-  auto is_EQ = dbl_is_equal; 
+  (void) numMissVals;
+  auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
 
   for (size_t i = 0; i < len; ++i) { v[i] = is_EQ(v[i], missval1) ? missval1 : std::fmod((double) v[i], divisor); }
@@ -171,9 +171,9 @@ void
 fieldc_mod(Field &field, double divisor)
 {
   if (field.memType == MemType::Float)
-    fieldc_mod(field.size, field.nmiss, field.vec_f, (float) field.missval, divisor);
+    fieldc_mod(field.size, field.numMissVals, field.vec_f, (float) field.missval, divisor);
   else
-    fieldc_mod(field.size, field.nmiss, field.vec_d, field.missval, divisor);
+    fieldc_mod(field.size, field.numMissVals, field.vec_d, field.missval, divisor);
 }
 
 void
diff --git a/src/fieldc_complex.cc b/src/fieldc_complex.cc
index 9c97402259dccc6347d7b40f76cc70c915087c03..d27581e1bf589549ce6ed9c310ab4950b7459a87 100644
--- a/src/fieldc_complex.cc
+++ b/src/fieldc_complex.cc
@@ -13,9 +13,9 @@
 
 template <typename T>
 static void
-fieldc_mul_complex(size_t len, size_t nmiss, Varray<T> &v, double missval, const double rconst[2])
+fieldc_mul_complex(size_t len, size_t numMissVals, Varray<T> &v, double missval, const double rconst[2])
 {
-  (void) nmiss;
+  (void) numMissVals;
   // z1 x z2 = (x1x2 - y1y2) + i(x1y2 + x2y1)
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
@@ -33,16 +33,16 @@ static void
 fieldc_mul_complex(Field &field, const double rconst[2])
 {
   if (field.memType == MemType::Float)
-    fieldc_mul_complex(field.size, field.nmiss, field.vec_f, field.missval, rconst);
+    fieldc_mul_complex(field.size, field.numMissVals, field.vec_f, field.missval, rconst);
   else
-    fieldc_mul_complex(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_mul_complex(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static void
-fieldc_div_complex(size_t len, size_t nmiss, Varray<T> &v, double missval, const double rconst[2])
+fieldc_div_complex(size_t len, size_t numMissVals, Varray<T> &v, double missval, const double rconst[2])
 {
-  (void) nmiss;
+  (void) numMissVals;
   // z1 / z2 = (x1x2 + y1y2) / (x2x2 + y2y2) + i (y1x2 - x1y2) / (x2x2 + y2y2)
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
@@ -61,16 +61,16 @@ static void
 fieldc_div_complex(Field &field, const double rconst[2])
 {
   if (field.memType == MemType::Float)
-    fieldc_div_complex(field.size, field.nmiss, field.vec_f, field.missval, rconst);
+    fieldc_div_complex(field.size, field.numMissVals, field.vec_f, field.missval, rconst);
   else
-    fieldc_div_complex(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_div_complex(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static void
-fieldc_add_complex(size_t len, size_t nmiss, Varray<T> &v, double missval, const double rconst[2])
+fieldc_add_complex(size_t len, size_t numMissVals, Varray<T> &v, double missval, const double rconst[2])
 {
-  (void) nmiss;
+  (void) numMissVals;
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
   auto missval2 = missval;
@@ -85,16 +85,16 @@ static void
 fieldc_add_complex(Field &field, const double rconst[2])
 {
   if (field.memType == MemType::Float)
-    fieldc_add_complex(field.size, field.nmiss, field.vec_f, field.missval, rconst);
+    fieldc_add_complex(field.size, field.numMissVals, field.vec_f, field.missval, rconst);
   else
-    fieldc_add_complex(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_add_complex(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 template <typename T>
 static void
-fieldc_sub_complex(size_t len, size_t nmiss, Varray<T> &v, double missval, const double rconst[2])
+fieldc_sub_complex(size_t len, size_t numMissVals, Varray<T> &v, double missval, const double rconst[2])
 {
-  (void) nmiss;
+  (void) numMissVals;
   auto is_EQ = dbl_is_equal;
   auto missval1 = missval;
   auto missval2 = missval;
@@ -109,9 +109,9 @@ static void
 fieldc_sub_complex(Field &field, const double rconst[2])
 {
   if (field.memType == MemType::Float)
-    fieldc_sub_complex(field.size, field.nmiss, field.vec_f, field.missval, rconst);
+    fieldc_sub_complex(field.size, field.numMissVals, field.vec_f, field.missval, rconst);
   else
-    fieldc_sub_complex(field.size, field.nmiss, field.vec_d, field.missval, rconst);
+    fieldc_sub_complex(field.size, field.numMissVals, field.vec_d, field.missval, rconst);
 }
 
 void
diff --git a/src/fileStream.cc b/src/fileStream.cc
index 620ace68799b76ecc0f56a4aa2ff9a1754adaf82..03cde10d0d3f4d531facab949c8c8ff9e88c6534 100644
--- a/src/fileStream.cc
+++ b/src/fileStream.cc
@@ -51,10 +51,7 @@ FileStream::open_read()
 
       fileID = streamOpenReadQuery(path.c_str(), query);
     }
-  else
-    {
-      fileID = streamOpenRead(m_filename.c_str());
-    }
+  else { fileID = streamOpenRead(m_filename.c_str()); }
 
   if (fileID < 0) cdi_open_error(fileID, "Open failed on >%s<", m_filename.c_str());
   isopen = true;
@@ -252,59 +249,59 @@ FileStream::defRecord(int varID, int levelID)
 }
 
 void
-FileStream::read_record(float *const p_data, size_t *const nmiss)
+FileStream::read_record(float *const p_data, size_t *const numMissVals)
 {
   if (FileStream::timersEnabled()) timer_start(timer_read);
-  stream_readrecord_float_locked(m_fileID, p_data, nmiss);
+  stream_readrecord_float_locked(m_fileID, p_data, numMissVals);
   if (FileStream::timersEnabled()) timer_stop(timer_read);
 }
 
 void
-FileStream::read_record(double *const p_data, size_t *const nmiss)
+FileStream::read_record(double *const p_data, size_t *const numMissVals)
 {
   if (FileStream::timersEnabled()) timer_start(timer_read);
-  stream_readrecord_double_locked(m_fileID, p_data, nmiss);
+  stream_readrecord_double_locked(m_fileID, p_data, numMissVals);
   if (FileStream::timersEnabled()) timer_stop(timer_read);
 }
 
 void
-FileStream::read_record(Field *const p_field, size_t *const nmiss)
+FileStream::read_record(Field *const p_field, size_t *const numMissVals)
 {
-  read_record(p_field->vec_d.data(), nmiss);
+  read_record(p_field->vec_d.data(), numMissVals);
 }
 
 void
-FileStream::write_record(const float *const p_data, size_t p_nmiss)
+FileStream::write_record(const float *const p_data, size_t p_numMissVals)
 {
   if (FileStream::timersEnabled()) timer_start(timer_write);
 
   auto varID = m_varID;
   if (varID < (int) m_datarangelist.size())
-    if (m_datarangelist[varID].checkDatarange) m_datarangelist[varID].check_datarange(p_data, p_nmiss);
+    if (m_datarangelist[varID].checkDatarange) m_datarangelist[varID].check_datarange(p_data, p_numMissVals);
 
-  stream_write_record_float_locked(m_fileID, p_data, p_nmiss);
+  stream_write_record_float_locked(m_fileID, p_data, p_numMissVals);
 
   if (FileStream::timersEnabled()) timer_stop(timer_write);
 }
 
 void
-FileStream::write_record(const double *const p_data, size_t p_nmiss)
+FileStream::write_record(const double *const p_data, size_t p_numMissVals)
 {
   if (FileStream::timersEnabled()) timer_start(timer_write);
 
   auto varID = m_varID;
   if (varID < (int) m_datarangelist.size())
-    if (m_datarangelist[varID].checkDatarange) m_datarangelist[varID].check_datarange(p_data, p_nmiss);
+    if (m_datarangelist[varID].checkDatarange) m_datarangelist[varID].check_datarange(p_data, p_numMissVals);
 
-  stream_write_record_double_locked(m_fileID, p_data, p_nmiss);
+  stream_write_record_double_locked(m_fileID, p_data, p_numMissVals);
 
   if (FileStream::timersEnabled()) timer_stop(timer_write);
 }
 
 void
-FileStream::write_record(const Field *const p_field, size_t p_nmiss)
+FileStream::write_record(const Field *const p_field, size_t p_numMissVals)
 {
-  write_record(p_field->vec_d.data(), p_nmiss);
+  write_record(p_field->vec_d.data(), p_numMissVals);
 }
 
 void
@@ -446,10 +443,7 @@ FileStream::defDatarangeList(int p_vlistID)
                   || datatype == CDI_DATATYPE_UINT16)
                 m_datarangelist[varID].checkDatarange = true;
             }
-          else if (Options::CheckDatarange)
-            {
-              m_datarangelist[varID].checkDatarange = true;
-            }
+          else if (Options::CheckDatarange) { m_datarangelist[varID].checkDatarange = true; }
         }
     }
 
diff --git a/src/fileStream.h b/src/fileStream.h
index f12b9c8f1b640a72f9bbf17ef6829bfbe40d97bd..7c210295692a40c0de9194d3a1470f43e3b0e49b 100644
--- a/src/fileStream.h
+++ b/src/fileStream.h
@@ -22,13 +22,13 @@ public:
   void inq_record(int *varID, int *levelID) override;
   void defRecord(int varID, int levelID) override;
 
-  void read_record(float *const p_data, size_t *nmiss) override;
-  void read_record(double *const p_data, size_t *nmiss) override;
-  void read_record(Field *const p_field, size_t *nmiss) override;
+  void read_record(float *const p_data, size_t *numMissVals) override;
+  void read_record(double *const p_data, size_t *numMissVals) override;
+  void read_record(Field *const p_field, size_t *numMissVals) override;
 
-  void write_record(const float *const p_data, size_t nmiss) override;
-  void write_record(const double *const p_data, size_t nmiss) override;
-  void write_record(const Field *const p_field, size_t nmiss) override;
+  void write_record(const float *const p_data, size_t numMissVals) override;
+  void write_record(const double *const p_data, size_t numMissVals) override;
+  void write_record(const Field *const p_field, size_t numMissVals) override;
 
   void copyRecord(CdoStreamID p_fileStream) override;
 
@@ -64,7 +64,7 @@ protected:
 private:
   FileStream() = delete;
   std::string m_filename;
-  void checkDatarange(int varID, double *array, size_t nmiss);
+  void checkDatarange(int varID, double *array, size_t numMissVals);
 };
 
 #endif
diff --git a/src/fill_1d.cc b/src/fill_1d.cc
index 7b5d08ebf0a46b4cd0798938d9c53ef6110a2141..a1a711c517a2390cd50c47b99b9f33cf58bd174b 100644
--- a/src/fill_1d.cc
+++ b/src/fill_1d.cc
@@ -24,7 +24,8 @@ string_to_fillmethod(const std::string &methodStr)
 }
 
 static void
-nearest_neighbour_with_lowest_delta(const Varray<double> &xValues, Varray<double> &yValues, int firstIndex, int lastIndex, int limit)
+nearest_neighbour_with_lowest_delta(const Varray<double> &xValues, Varray<double> &yValues, int firstIndex, int lastIndex,
+                                    int limit)
 {
   auto startIndex = firstIndex + 1;
   auto endIndex = (limit > 0) ? std::min(lastIndex, startIndex + limit) : lastIndex;
@@ -89,15 +90,9 @@ fill_1d_nearest(int numValues, const Varray<double> &timeValues, Varray<double>
               for (int k = startIndex; k < endIndex; ++k) dataValues[k] = dataValues[firstIndex];
               break;
             }
-          else if (firstIndex == UndefIndex && lastIndex == UndefIndex)
-            {
-              break;
-            }
-        }
-      else
-        {
-          firstIndex = i;
+          else if (firstIndex == UndefIndex && lastIndex == UndefIndex) { break; }
         }
+      else { firstIndex = i; }
     }
 }
 
@@ -148,15 +143,9 @@ fill_1d_linear(int numValues, const Varray<double> &timeValues, Varray<double> &
               firstIndex = lastIndex;
               lastIndex = UndefIndex;
             }
-          else if (lastIndex == UndefIndex)
-            {
-              break;
-            }
-        }
-      else
-        {
-          firstIndex = i;
+          else if (lastIndex == UndefIndex) { break; }
         }
+      else { firstIndex = i; }
     }
 }
 
@@ -208,15 +197,9 @@ fill_1d_forward(int numValues, Varray<double> &dataValues, double missval, int l
               for (int k = startIndex; k < endIndex; ++k) dataValues[k] = dataValues[firstIndex];
               break;
             }
-          else if (firstIndex == UndefIndex && lastIndex == UndefIndex)
-            {
-              break;
-            }
-        }
-      else
-        {
-          firstIndex = i;
+          else if (firstIndex == UndefIndex && lastIndex == UndefIndex) { break; }
         }
+      else { firstIndex = i; }
     }
 }
 
@@ -263,14 +246,8 @@ fill_1d_backward(int numValues, Varray<double> &dataValues, double missval, int
               firstIndex = lastIndex;
               lastIndex = UndefIndex;
             }
-          else if (lastIndex == UndefIndex)
-            {
-              break;
-            }
-        }
-      else
-        {
-          firstIndex = i;
+          else if (lastIndex == UndefIndex) { break; }
         }
+      else { firstIndex = i; }
     }
 }
diff --git a/src/fill_1d.h b/src/fill_1d.h
index bd854be3edb14a95682eff4090e28320ceac2007..a6a3d3a8b4ee14c96d99db93212adcc62e266ecb 100644
--- a/src/fill_1d.h
+++ b/src/fill_1d.h
@@ -20,8 +20,10 @@ enum class FillMethod
 
 FillMethod string_to_fillmethod(const std::string &methodStr);
 
-void fill_1d_nearest(int numValues, const Varray<double> &timeValues, Varray<double> &dataValues, double missval, int limit, int maxGaps);
-void fill_1d_linear(int numValues, const Varray<double> &timeValues, Varray<double> &dataValues, double missval, int limit, int maxGaps);
+void fill_1d_nearest(int numValues, const Varray<double> &timeValues, Varray<double> &dataValues, double missval, int limit,
+                     int maxGaps);
+void fill_1d_linear(int numValues, const Varray<double> &timeValues, Varray<double> &dataValues, double missval, int limit,
+                    int maxGaps);
 void fill_1d_forward(int numValues, Varray<double> &dataValues, double missval, int limit, int maxGaps);
 void fill_1d_backward(int numValues, Varray<double> &dataValues, double missval, int limit, int maxGaps);
 
diff --git a/src/grid_area.cc b/src/grid_area.cc
index 6b8d02db42be54b1c2e210f302b776f29ee29be7..6e7ee2d1ac52899a5471c5e7c8fdbb592bdc1a32 100644
--- a/src/grid_area.cc
+++ b/src/grid_area.cc
@@ -502,19 +502,13 @@ gridGenArea(int gridID, double *area)
       bool lweights = false;
       status = gen_gridcellarea_reg2d(gridID, area, lweights);
     }
-  else if (is_healpix_grid(gridID))
-    {
-      status = gen_gridcellarea_healpix(gridID, area);
-    }
+  else if (is_healpix_grid(gridID)) { status = gen_gridcellarea_healpix(gridID, area); }
   else if (gridProjIsSupported(gridID) || gridtype == GRID_GME || gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED
            || gridtype == GRID_GAUSSIAN_REDUCED)
     {
       status = gen_gridcellarea_unstruct(gridID, area);
     }
-  else
-    {
-      cdo_abort("Internal error! Unsupported gridtype: %s", gridNamePtr(gridtype));
-    }
+  else { cdo_abort("Internal error! Unsupported gridtype: %s", gridNamePtr(gridtype)); }
 
   if (gridVerbose) cdo_print("Total area = %g steradians", array_sum(gridsize, area));
 
diff --git a/src/grid_from_name.cc b/src/grid_from_name.cc
index a4b7ed15b9209a8ebb695d1c1a5a09064d158d02..72a435e414898e4cf2fcda5543747efaba16cd38 100644
--- a/src/grid_from_name.cc
+++ b/src/grid_from_name.cc
@@ -93,7 +93,8 @@ generate_grid_zonal(GridDesciption &grid, const std::string &gridname, double in
 }
 
 static void
-generate_grid_lonlat(GridDesciption &grid, const std::string &gridname, double inc, double lon1, double lon2, double lat1, double lat2)
+generate_grid_lonlat(GridDesciption &grid, const std::string &gridname, double inc, double lon1, double lon2, double lat1,
+                     double lat2)
 {
   int gridtype = GRID_LONLAT;
   bool withBounds = true;
diff --git a/src/grid_point_search.cc b/src/grid_point_search.cc
index 0207e25144cd502cb8ce09175975103664dace4a..aff11900aa98252c398f6f7fa4edd9001bb1f67e 100644
--- a/src/grid_point_search.cc
+++ b/src/grid_point_search.cc
@@ -21,6 +21,7 @@
 #include "nanoflann.hpp"
 extern "C"
 {
+#include "lib/yac/grid_cell.h"
 #include "lib/yac/sphere_part.h"
 }
 
@@ -695,7 +696,8 @@ grid_point_search_nearest(const GridPointSearch &gps, double lon, double lat, si
 }
 
 static size_t
-gps_qnearest_kdtree(const GridPointSearch &gps, double lon, double lat, double searchRadius, size_t nnn, size_t *indices, double *dist)
+gps_qnearest_kdtree(const GridPointSearch &gps, double lon, double lat, double searchRadius, size_t nnn, size_t *indices,
+                    double *dist)
 {
   size_t numIndices = 0;
 
diff --git a/src/grid_print.cc b/src/grid_print.cc
index 5ecf44f7ada43ae4263cebefa70644f74bafb441..c9cfba6d1b55b42ca65beed0440b2ae82c0c1661 100644
--- a/src/grid_print.cc
+++ b/src/grid_print.cc
@@ -136,7 +136,6 @@ grid_print_attributes(FILE *fp, int gridID)
 static void
 grid_print_kernel(int gridID, int opt, FILE *fp)
 {
-  size_t xdim, ydim;
   auto nxvals = gridInqXvals(gridID, nullptr);
   auto nyvals = gridInqYvals(gridID, nullptr);
   auto nxbounds = gridInqXbounds(gridID, nullptr);
@@ -200,28 +199,36 @@ grid_print_kernel(int gridID, int opt, FILE *fp)
           if (ysize > 0) fprintf(fp, "ysize     = %zu\n", ysize);
         }
 
+      auto xdimname = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_DIMNAME);
       if (nxvals > 0 || xcvals)
         {
-          auto name = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_NAME);
-          auto dimname = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_DIMNAME);
-          auto longname = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_LONGNAME);
-          auto units = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_UNITS);
-          if (name.size()) fprintf(fp, "xname     = %s\n", name.c_str());
-          if (dimname.size() && dimname != name) fprintf(fp, "xdimname  = %s\n", dimname.c_str());
-          if (longname.size()) fprintf(fp, "xlongname = \"%s\"\n", longname.c_str());
-          if (units.size()) fprintf(fp, "xunits    = \"%s\"\n", units.c_str());
+          auto xname = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_NAME);
+          auto xlongname = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_LONGNAME);
+          auto xunits = cdo::inq_key_string(gridID, CDI_XAXIS, CDI_KEY_UNITS);
+          if (xname.size()) fprintf(fp, "xname     = %s\n", xname.c_str());
+          if (xdimname.size() && xdimname != xname) fprintf(fp, "xdimname  = %s\n", xdimname.c_str());
+          if (xlongname.size()) fprintf(fp, "xlongname = \"%s\"\n", xlongname.c_str());
+          if (xunits.size()) fprintf(fp, "xunits    = \"%s\"\n", xunits.c_str());
+        }
+      else if (xsize > 0)
+        {
+          if (xdimname.size()) fprintf(fp, "xdimname  = %s\n", xdimname.c_str());
         }
 
+      auto ydimname = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_DIMNAME);
       if (nyvals > 0 || ycvals)
         {
-          auto name = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_NAME);
-          auto dimname = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_DIMNAME);
-          auto longname = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_LONGNAME);
-          auto units = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_UNITS);
-          if (name.size()) fprintf(fp, "yname     = %s\n", name.c_str());
-          if (dimname.size() && dimname != name) fprintf(fp, "ydimname  = %s\n", dimname.c_str());
-          if (longname.size()) fprintf(fp, "ylongname = \"%s\"\n", longname.c_str());
-          if (units.size()) fprintf(fp, "yunits    = \"%s\"\n", units.c_str());
+          auto yname = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_NAME);
+          auto ylongname = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_LONGNAME);
+          auto yunits = cdo::inq_key_string(gridID, CDI_YAXIS, CDI_KEY_UNITS);
+          if (yname.size()) fprintf(fp, "yname     = %s\n", yname.c_str());
+          if (ydimname.size() && ydimname != yname) fprintf(fp, "ydimname  = %s\n", ydimname.c_str());
+          if (ylongname.size()) fprintf(fp, "ylongname = \"%s\"\n", ylongname.c_str());
+          if (yunits.size()) fprintf(fp, "yunits    = \"%s\"\n", yunits.c_str());
+        }
+      else if (ysize > 0)
+        {
+          if (ydimname.size()) fprintf(fp, "ydimname  = %s\n", ydimname.c_str());
         }
 
       if (type == GRID_UNSTRUCTURED || type == GRID_CURVILINEAR)
@@ -244,6 +251,8 @@ grid_print_kernel(int gridID, int opt, FILE *fp)
     case GRID_TRAJECTORY:
     case GRID_CHARXY:
       {
+        size_t xdim, ydim;
+
         if (type == GRID_GAUSSIAN || type == GRID_GAUSSIAN_REDUCED) fprintf(fp, "numLPE    = %d\n", gridInqNP(gridID));
 
         if (type == GRID_CURVILINEAR || type == GRID_UNSTRUCTURED)
diff --git a/src/grid_read.cc b/src/grid_read.cc
index b3d21fa72cac33e8e061b30f5c63d85279c757d8..862284100f6d4fa13be879ccfb32fd505811df30 100644
--- a/src/grid_read.cc
+++ b/src/grid_read.cc
@@ -9,7 +9,6 @@
 #include "cdi_uuid.h"
 
 #include "cdo_output.h"
-#include "readline.h"
 #include "param_conversion.h"
 #include "griddes.h"
 #include "parse_literals.h"
diff --git a/src/grid_read_pingo.cc b/src/grid_read_pingo.cc
index 0f9ac9e5b3ee7c434e15f7f09899b6f11049ee7c..0c2370db1ef62859cac748d9536bed98266eaec5 100644
--- a/src/grid_read_pingo.cc
+++ b/src/grid_read_pingo.cc
@@ -25,7 +25,7 @@ skip_nondigit_lines(FILE *gfp)
   while (1)
     {
       do c = fgetc(gfp);
-      while ((isspace(c) || c == ',') && c != EOF);
+      while ((std::isspace(c) || c == ',') && c != EOF);
 
       if (c == EOF || std::isdigit(c) || c == '.' || c == '+' || c == '-' || c == 'N')
         break;  // N: for NaN
diff --git a/src/griddes.cc b/src/griddes.cc
index 82e31e10e4ce31b90795025f001ea5af3e6c1928..66f6f0b79da7f5ef4c4fd1f61a3a3553a5e0806d 100644
--- a/src/griddes.cc
+++ b/src/griddes.cc
@@ -350,16 +350,16 @@ gridFromMPAS(const char *filename)
       auto nrecs = streamInqTimestep(streamID, 0);
       for (int recID = 0; recID < nrecs; ++recID)
         {
-          size_t nmiss;
+          size_t numMissVals;
           int varID, levelID;
           streamInqRecord(streamID, &varID, &levelID);
           // clang-format off
-          if      (varID == latCellID)   streamReadRecord(streamID, latCell.data(), &nmiss);
-          else if (varID == lonCellID)   streamReadRecord(streamID, lonCell.data(), &nmiss);
-          else if (varID == latVertexID) streamReadRecord(streamID, latVertex.data(), &nmiss);
-          else if (varID == lonVertexID) streamReadRecord(streamID, lonVertex.data(), &nmiss);
-          else if (varID == areaCellID)  streamReadRecord(streamID, areaCell.data(), &nmiss);
-          else if (varID == verticesOnCellID) streamReadRecord(streamID, verticesOnCell.data(), &nmiss);
+          if      (varID == latCellID)   streamReadRecord(streamID, latCell.data(), &numMissVals);
+          else if (varID == lonCellID)   streamReadRecord(streamID, lonCell.data(), &numMissVals);
+          else if (varID == latVertexID) streamReadRecord(streamID, latVertex.data(), &numMissVals);
+          else if (varID == lonVertexID) streamReadRecord(streamID, lonVertex.data(), &numMissVals);
+          else if (varID == areaCellID)  streamReadRecord(streamID, areaCell.data(), &numMissVals);
+          else if (varID == verticesOnCellID) streamReadRecord(streamID, verticesOnCell.data(), &numMissVals);
           // clang-format on
         }
       streamClose(streamID);
@@ -398,10 +398,7 @@ gridFromMPAS(const char *filename)
                     xc[k] = xc[k - 1];
                     yc[k] = yc[k - 1];
                   }
-                else
-                  {
-                    withBounds = false;
-                  }
+                else { withBounds = false; }
               }
             else
               {
@@ -482,7 +479,7 @@ cdo_define_grid(const char *pgridfile)
 
       close(fileno);
 
-      if (buffer[0] == 'C' && buffer[1] == 'D' && buffer[2] == 'F') // CDF
+      if (buffer[0] == 'C' && buffer[1] == 'D' && buffer[2] == 'F')  // CDF
         {
           Debug(cdoDebug, "Grid from NetCDF file");
           gridID = grid_from_nc_file(filename);
@@ -490,7 +487,7 @@ cdo_define_grid(const char *pgridfile)
 
       if (gridID == -1)
         {
-          if (buffer[1] == 'H' && buffer[2] == 'D' && buffer[3] == 'F') // HDF
+          if (buffer[1] == 'H' && buffer[2] == 'D' && buffer[3] == 'F')  // HDF
             {
               Debug(cdoDebug, "Grid from HDF5 file");
               gridID = grid_from_h5_file(filename);
@@ -499,7 +496,7 @@ cdo_define_grid(const char *pgridfile)
 
       if (gridID == -1)
         {
-          if (buffer[1] == 'H' && buffer[2] == 'D' && buffer[3] == 'F') // HDF
+          if (buffer[1] == 'H' && buffer[2] == 'D' && buffer[3] == 'F')  // HDF
             {
               Debug(cdoDebug, "Grid from NetCDF4 file");
               gridID = grid_from_nc_file(filename);
diff --git a/src/griddes_h5.cc b/src/griddes_h5.cc
index 87f17dc3080bd7ad731f3332bd67ac2a69ff5f3c..658e4dc68e4ae618d41a6b831d58dede6e3b9f79 100644
--- a/src/griddes_h5.cc
+++ b/src/griddes_h5.cc
@@ -242,7 +242,7 @@ grid_from_h5_file(const char *gridfile)
   hid_t lat_id = -1;
   hid_t att_id;
   hid_t dataspace;
-  hsize_t dims_out[9]; // dataset dimensions
+  hsize_t dims_out[9];  // dataset dimensions
   [[maybe_unused]] herr_t status;
   int rank;
   GridDesciption grid;
diff --git a/src/hetaeta.cc b/src/hetaeta.cc
index 0a6704b12cb783b54e5668f02aabefe4f03066d2..90302290020457f7d3c9a95ac648524bc1796e8d 100644
--- a/src/hetaeta.cc
+++ b/src/hetaeta.cc
@@ -5,7 +5,7 @@
 
 */
 
-//#define  OUTPUT 1
+// #define  OUTPUT 1
 
 #ifdef _OPENMP
 #include <omp.h>
diff --git a/src/hetaeta.h b/src/hetaeta.h
index 7042b38d0c898cb2589de902b6c2453cccf5e919..8696be341e9fd92db5a8ee34aec92686eda4e637 100644
--- a/src/hetaeta.h
+++ b/src/hetaeta.h
@@ -1,6 +1,8 @@
 #ifndef _HETAETA_H
 #define _HETAETA_H
 
+#include "varray.h"
+
 template <typename T>
 void hetaeta(bool ltq, int ngp, const int *imiss, int nlev1, const double *ah1, const double *bh1, const Varray<double> &fis1,
              const Varray<double> &ps1, const Varray<T> &t1, const Varray<T> &q1, int nlev2, const double *ah2, const double *bh2,
diff --git a/src/institution.cc b/src/institution.cc
index 8e1668b9f2f28167c6947019fe63d141cd27de65..8a74c8e16b2586f038d7ac4d1a85279706c5c3b6 100644
--- a/src/institution.cc
+++ b/src/institution.cc
@@ -5,67 +5,66 @@
 
 */
 
+#include <fstream>
+
 #include <cdi.h>
 
 #include "process_int.h"
-#include "readline.h"
 #include "cdo_default_values.h"
 
 static int
-readInstitution(const char *instfile)
+read_institution(const std::string &filename)
 {
-  char line[1024];
   int lnr = 0;
   int nvar = 0, maxvar = 4;
-  char name[1024], longname[1024];
   int center = CDI_UNDEFID, subcenter = CDI_UNDEFID;
+  std::string name, longname;
 
-  auto instfp = std::fopen(instfile, "r");
-  if (instfp == nullptr) return CDI_UNDEFID;
+  std::ifstream file(filename);
+  if (!file.is_open()) cdo_abort("Open failed on: %s\n", filename);
 
-  while (cdo::readline(instfp, line, 1024))
+  std::string line;
+  while (std::getline(file, line))
     {
       lnr++;
       if (line[0] == '#') continue;
       if (nvar == maxvar) break;
       nvar++;
 
-      char *pline = line;
-      while (isspace((int) *pline)) pline++;
+      while (std::isspace((int) line[0])) line.erase(0, 1);
 
-      if (nvar == 1) maxvar = std::isdigit((int) pline[0]) ? 4 : 2;
+      if (nvar == 1) maxvar = std::isdigit((int) line[0]) ? 4 : 2;
 
-      if (nvar == 1 && maxvar == 4) center = atoi(pline);
+      if (nvar == 1 && maxvar == 4) center = atoi(line.c_str());
 
       if (nvar == 2 && maxvar == 4)
         {
-          if (!std::isdigit((int) pline[0])) cdo_abort("wrong format in line %d. Missing subcenter!", lnr);
+          if (!std::isdigit((int) line[0])) cdo_abort("wrong format in line %d. Missing subcenter!", lnr);
 
-          subcenter = atoi(pline);
+          subcenter = atoi(line.c_str());
         }
 
-      if ((nvar == 3 && maxvar == 4) || (nvar == 1 && maxvar == 2)) std::strcpy(name, pline);
+      if ((nvar == 3 && maxvar == 4) || (nvar == 1 && maxvar == 2)) name = line;
 
-      if ((nvar == 4 && maxvar == 4) || (nvar == 2 && maxvar == 2)) std::strcpy(longname, pline);
+      if ((nvar == 4 && maxvar == 4) || (nvar == 2 && maxvar == 2)) longname = line;
     }
 
-  std::fclose(instfp);
+  file.close();
 
-  auto instID = institutInq(center, subcenter, name, longname);
-  if (instID == CDI_UNDEFID) instID = institutDef(center, subcenter, name, longname);
+  auto instID = institutInq(center, subcenter, name.c_str(), longname.c_str());
+  if (instID == CDI_UNDEFID) instID = institutDef(center, subcenter, name.c_str(), longname.c_str());
 
   return instID;
 }
 
 void
-define_institution(const char *instarg)
+define_institution(const std::string &instString)
 {
-  const char *instname = instarg;
-  int instID = readInstitution(instname);
+  int instID = read_institution(instString);
 
-  if (instID == CDI_UNDEFID) instID = institutInq(0, 0, instname, nullptr);
+  if (instID == CDI_UNDEFID) instID = institutInq(0, 0, instString.c_str(), nullptr);
 
-  if (instID == CDI_UNDEFID) cdo_abort("institution <%s> not found", instname);
+  if (instID == CDI_UNDEFID) cdo_abort("institution <%s> not found", instString);
 
   CdoDefault::InstID = instID;
 }
diff --git a/src/institution.h b/src/institution.h
index 327d1b7c023e17148567dc2d398acb9116abf7ea..3c27a3163ff26d64d68c09f9e60032d9c699f9a6 100644
--- a/src/institution.h
+++ b/src/institution.h
@@ -1,6 +1,8 @@
 #ifndef CDO_INSTITUTION
 #define CDO_INSTITUTION
 
-void define_institution(const char *instarg);
+#include <string>
+
+void define_institution(const std::string &instString);
 
 #endif
diff --git a/src/interpol.cc b/src/interpol.cc
index be819b67a7cb482f3ab01811b5c15a3c4b4ff3b9..3ed2a32d9f2eb91df6ee5408e9c094d7b611657e 100644
--- a/src/interpol.cc
+++ b/src/interpol.cc
@@ -178,7 +178,7 @@ rect_grid_search2(long &imin, long &imax, double xmin, double xmax, long nxm, co
   return lfound;
 }
 
-static double
+double
 intlinarr2p(long nxm, long nym, double **fieldm, const Varray<double> &xm, const Varray<double> &ym, double x, double y)
 {
   long ii, jj;
@@ -811,5 +811,5 @@ interpolate(const Field &field1, Field &field2)
         }
     }
 
-  field2.nmiss = varray_num_mv(gridsize_o, arrayOut, missval);
+  field2.numMissVals = varray_num_mv(gridsize_o, arrayOut, missval);
 }
diff --git a/src/lib/gradsdes/CMakeLists.txt b/src/lib/gradsdes/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a3b7ec43fac5059c27e678ac58f0693089af6096
--- /dev/null
+++ b/src/lib/gradsdes/CMakeLists.txt
@@ -0,0 +1,8 @@
+list( APPEND gradsdes_src_files
+  gradsdes.c
+  gradsdes.h
+)
+
+add_library(gradsdes
+  ${gradsdes_src_files}
+)
diff --git a/src/lib/gradsdes/gradsdes.c b/src/lib/gradsdes/gradsdes.c
index 2f62a1f85edc9624514de4ef447dc294978a50b3..2b29cdfdcade2011f2c9726432f474772489c628 100644
--- a/src/lib/gradsdes/gradsdes.c
+++ b/src/lib/gradsdes/gradsdes.c
@@ -464,7 +464,7 @@ adtprs(char *ch, struct dt *def, struct dt *dtim)
           if (val > 23)
             {
               gaprnt(0, "Syntax Error:  Invalid Date/Time value.\n");
-              sprintf(pout, "  Hour = %i -- greater than 23\n", val);
+              snprintf(pout, sizeof(pout), "  Hour = %i -- greater than 23\n", val);
               gaprnt(0, pout);
               return (NULL);
             }
@@ -478,7 +478,7 @@ adtprs(char *ch, struct dt *def, struct dt *dtim)
                   if (val > 59)
                     {
                       gaprnt(0, "Syntax Error:  Invalid Date/Time value.\n");
-                      sprintf(pout, "  Minute = %i -- greater than 59\n", val);
+                      snprintf(pout, sizeof(pout), "  Minute = %i -- greater than 59\n", val);
                       gaprnt(0, pout);
                       return (NULL);
                     }
@@ -583,7 +583,7 @@ adtprs(char *ch, struct dt *def, struct dt *dtim)
   if (dtim->dy > i)
     {
       gaprnt(0, "Syntax Error:  Invalid Date/Time value.\n");
-      sprintf(pout, "  Day = %i -- greater than %i \n", dtim->dy, i);
+      snprintf(pout, sizeof(pout), "  Day = %i -- greater than %i \n", dtim->dy, i);
       gaprnt(0, pout);
       return (NULL);
     }
@@ -634,7 +634,7 @@ rdtprs(char *ch, struct dt *dtim)
       else
         {
           gaprnt(0, "Syntax Error:  Invalid Date/Time offset.\n");
-          sprintf(pout, "  Expecting yr/mo/dy/hr/mn, found %s\n", id);
+          snprintf(pout, sizeof(pout), "  Expecting yr/mo/dy/hr/mn, found %s\n", id);
           gaprnt(0, pout);
           return (NULL);
         }
@@ -1155,6 +1155,7 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
   olen = 0;
   while (*(fn + olen)) olen++;
   olen += 5;
+  size_t outLen = olen;
   fnout = (char *) galloc(olen, "fnout");
   if (fnout == NULL) return (NULL);
 
@@ -1171,13 +1172,18 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
           tused = 1;
           if (*(in + 2) == 'x' && *(in + 3) == '1')
             {
-              sprintf(out, "%i", dtimi->yr / 10);
-              while (*out) out++;
+              snprintf(out, outLen, "%i", dtimi->yr / 10);
+              while (*out)
+                {
+                  outLen--;
+                  out++;
+                }
               in += 4;
             }
           else if (*(in + 2) == 'x' && *(in + 3) == '3')
             {
-              sprintf(out, "%03i", dtimi->yr / 10);
+              snprintf(out, outLen, "%03i", dtimi->yr / 10);
+              outLen -= 3;
               out += 3;
               in += 4;
             }
@@ -1185,25 +1191,32 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
             {
               iv = dtimi->yr / 100;
               iv = dtimi->yr - iv * 100;
-              sprintf(out, "%02i", iv);
+              snprintf(out, outLen, "%02i", iv);
+              outLen -= 2;
               out += 2;
               in += 4;
             }
           else if (*(in + 2) == 'y' && *(in + 3) == '4')
             {
-              sprintf(out, "%04i", dtimi->yr);
+              snprintf(out, outLen, "%04i", dtimi->yr);
+              outLen -= 4;
               out += 4;
               in += 4;
             }
           else if (*(in + 2) == 'm' && *(in + 3) == '1')
             {
-              sprintf(out, "%i", dtimi->mo);
-              while (*out) out++;
+              snprintf(out, outLen, "%i", dtimi->mo);
+              while (*out)
+                {
+                  outLen--;
+                  out++;
+                }
               in += 4;
             }
           else if (*(in + 2) == 'm' && *(in + 3) == '2')
             {
-              sprintf(out, "%02i", dtimi->mo);
+              snprintf(out, outLen, "%02i", dtimi->mo);
+              outLen -= 2;
               out += 2;
               in += 4;
             }
@@ -1213,6 +1226,7 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
                 *out = 'a';
               else
                 *out = 'b';
+              outLen -= 1;
               out += 1;
               in += 4;
             }
@@ -1222,6 +1236,7 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
                 *out = 'A';
               else
                 *out = 'B';
+              outLen -= 1;
               out += 1;
               in += 4;
             }
@@ -1230,42 +1245,55 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
               *out = *(mons[dtimi->mo - 1]);
               *(out + 1) = *(mons[dtimi->mo - 1] + 1);
               *(out + 2) = *(mons[dtimi->mo - 1] + 2);
+              outLen -= 3;
               out += 3;
               in += 4;
             }
           else if (*(in + 2) == 'd' && *(in + 3) == '1')
             {
-              sprintf(out, "%i", dtimi->dy);
-              while (*out) out++;
+              snprintf(out, outLen, "%i", dtimi->dy);
+              while (*out)
+                {
+                  outLen--;
+                  out++;
+                }
               in += 4;
             }
           else if (*(in + 2) == 'd' && *(in + 3) == '2')
             {
-              sprintf(out, "%02i", dtimi->dy);
+              snprintf(out, outLen, "%02i", dtimi->dy);
+              outLen -= 2;
               out += 2;
               in += 4;
             }
           else if (*(in + 2) == 'h' && *(in + 3) == '1')
             {
-              sprintf(out, "%i", dtimi->hr);
-              while (*out) out++;
+              snprintf(out, outLen, "%i", dtimi->hr);
+              while (*out)
+                {
+                  outLen--;
+                  out++;
+                }
               in += 4;
             }
           else if (*(in + 2) == 'h' && *(in + 3) == '2')
             {
-              sprintf(out, "%02i", dtimi->hr);
+              snprintf(out, outLen, "%02i", dtimi->hr);
+              outLen -= 2;
               out += 2;
               in += 4;
             }
           else if (*(in + 2) == 'h' && *(in + 3) == '3')
             {
-              sprintf(out, "%03i", dtimi->hr);
+              snprintf(out, outLen, "%03i", dtimi->hr);
+              outLen -= 3;
               out += 3;
               in += 4;
             }
           else if (*(in + 2) == 'n' && *(in + 3) == '2')
             {
-              sprintf(out, "%02i", dtimi->mn);
+              snprintf(out, outLen, "%02i", dtimi->mn);
+              outLen -= 2;
               out += 2;
               in += 4;
             }
@@ -1280,14 +1308,19 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
       else if (*in == '%' && *(in + 1) == 'x' && *(in + 2) == '1')
         { /* x: decades */
           tused = 1;
-          sprintf(out, "%i", dtim->yr / 10);
-          while (*out) out++;
+          snprintf(out, outLen, "%i", dtim->yr / 10);
+          while (*out)
+            {
+              outLen--;
+              out++;
+            }
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'x' && *(in + 2) == '3')
         {
           tused = 1;
-          sprintf(out, "%03i", dtim->yr / 10);
+          snprintf(out, outLen, "%03i", dtim->yr / 10);
+          outLen -= 3;
           out += 3;
           in += 3;
         }
@@ -1296,28 +1329,35 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
           tused = 1;
           iv = dtim->yr / 100;
           iv = dtim->yr - iv * 100;
-          sprintf(out, "%02i", iv);
+          snprintf(out, outLen, "%02i", iv);
+          outLen -= 2;
           out += 2;
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'y' && *(in + 2) == '4')
         {
           tused = 1;
-          sprintf(out, "%04i", dtim->yr);
+          snprintf(out, outLen, "%04i", dtim->yr);
+          outLen -= 4;
           out += 4;
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'm' && *(in + 2) == '1')
         {
           tused = 1;
-          sprintf(out, "%i", dtim->mo);
-          while (*out) out++;
+          snprintf(out, outLen, "%i", dtim->mo);
+          while (*out)
+            {
+              outLen--;
+              out++;
+            }
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'm' && *(in + 2) == '2')
         {
           tused = 1;
-          sprintf(out, "%02i", dtim->mo);
+          snprintf(out, outLen, "%02i", dtim->mo);
+          outLen -= 2;
           out += 2;
           in += 3;
         }
@@ -1328,6 +1368,7 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
             *out = 'a';
           else
             *out = 'b';
+          outLen -= 1;
           out += 1;
           in += 3;
         }
@@ -1338,6 +1379,7 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
             *out = 'A';
           else
             *out = 'B';
+          outLen -= 1;
           out += 1;
           in += 3;
         }
@@ -1347,48 +1389,61 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
           *out = *(mons[dtim->mo - 1]);
           *(out + 1) = *(mons[dtim->mo - 1] + 1);
           *(out + 2) = *(mons[dtim->mo - 1] + 2);
+          outLen -= 3;
           out += 3;
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'd' && *(in + 2) == '1')
         {
           tused = 1;
-          sprintf(out, "%i", dtim->dy);
-          while (*out) out++;
+          snprintf(out, outLen, "%i", dtim->dy);
+          while (*out)
+            {
+              outLen--;
+              out++;
+            }
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'd' && *(in + 2) == '2')
         {
           tused = 1;
-          sprintf(out, "%02i", dtim->dy);
+          snprintf(out, outLen, "%02i", dtim->dy);
+          outLen -= 2;
           out += 2;
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'h' && *(in + 2) == '1')
         {
           tused = 1;
-          sprintf(out, "%i", dtim->hr);
-          while (*out) out++;
+          snprintf(out, outLen, "%i", dtim->hr);
+          while (*out)
+            {
+              outLen--;
+              out++;
+            }
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'h' && *(in + 2) == '2')
         {
           tused = 1;
-          sprintf(out, "%02i", dtim->hr);
+          snprintf(out, outLen, "%02i", dtim->hr);
+          outLen -= 2;
           out += 2;
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'h' && *(in + 2) == '3')
         {
           tused = 1;
-          sprintf(out, "%03i", dtim->hr);
+          snprintf(out, outLen, "%03i", dtim->hr);
+          outLen -= 3;
           out += 3;
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'n' && *(in + 2) == '2')
         {
           tused = 1;
-          sprintf(out, "%02i", dtim->mn);
+          snprintf(out, outLen, "%02i", dtim->mn);
+          outLen -= 2;
           out += 2;
           in += 3;
         }
@@ -1399,10 +1454,14 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
           tdif = timdif(dtimi, dtim);
           tdif = (tdif + 30) / 60;
           if (tdif < 99)
-            sprintf(out, "%02i", tdif);
+            snprintf(out, outLen, "%02i", tdif);
           else
-            sprintf(out, "%i", tdif);
-          while (*out) out++;
+            snprintf(out, outLen, "%i", tdif);
+          while (*out)
+            {
+              outLen--;
+              out++;
+            }
           in += 3;
         }
       else if (*in == '%' && *(in + 1) == 'f' && *(in + 2) == '3')
@@ -1411,10 +1470,14 @@ gafndt(char *fn, struct dt *dtim, struct dt *dtimi, gadouble *vals, struct gachs
           tdif = timdif(dtimi, dtim);
           tdif = (tdif + 30) / 60;
           if (tdif < 999)
-            sprintf(out, "%03i", tdif);
+            snprintf(out, outLen, "%03i", tdif);
           else
-            sprintf(out, "%i", tdif);
-          while (*out) out++;
+            snprintf(out, outLen, "%i", tdif);
+          while (*out)
+            {
+              outLen--;
+              out++;
+            }
           in += 3;
         }
       /* string substitution */
@@ -1850,8 +1913,8 @@ read_gradsdes(char *filename, dsets_t *pfi)
           if ((pos = intprs(ch, &(pfi->dnum[0]))) == NULL) goto err1;
           if (pfi->dnum[0] < 1)
             {
-              sprintf(pout, "Warning: Invalid XDEF syntax in %s -- Changing size of X axis from %d to 1 \n", pfi->dnam,
-                      pfi->dnum[0]);
+              snprintf(pout, sizeof(pout), "Warning: Invalid XDEF syntax in %s -- Changing size of X axis from %d to 1 \n",
+                       pfi->dnam, pfi->dnum[0]);
               gaprnt(1, pout);
               pfi->dnum[0] = 1;
             }
@@ -1884,8 +1947,8 @@ read_gradsdes(char *filename, dsets_t *pfi)
           if ((pos = intprs(ch, &(pfi->dnum[1]))) == NULL) goto err1;
           if (pfi->dnum[1] < 1)
             {
-              sprintf(pout, "Warning: Invalid YDEF syntax in %s -- Changing size of Y axis from %d to 1 \n", pfi->dnam,
-                      pfi->dnum[1]);
+              snprintf(pout, sizeof(pout), "Warning: Invalid YDEF syntax in %s -- Changing size of Y axis from %d to 1 \n",
+                       pfi->dnam, pfi->dnum[1]);
               gaprnt(1, pout);
               pfi->dnum[1] = 1;
             }
@@ -1911,8 +1974,8 @@ read_gradsdes(char *filename, dsets_t *pfi)
           if ((pos = intprs(ch, &(pfi->dnum[2]))) == NULL) goto err1;
           if (pfi->dnum[2] < 1)
             {
-              sprintf(pout, "Warning: Invalid ZDEF syntax in %s -- Changing size of Z axis from %d to 1 \n", pfi->dnam,
-                      pfi->dnum[2]);
+              snprintf(pout, sizeof(pout), "Warning: Invalid ZDEF syntax in %s -- Changing size of Z axis from %d to 1 \n",
+                       pfi->dnam, pfi->dnum[2]);
               gaprnt(1, pout);
               pfi->dnum[2] = 1;
             }
@@ -1939,8 +2002,8 @@ read_gradsdes(char *filename, dsets_t *pfi)
           if ((pos = intprs(ch, &(pfi->dnum[3]))) == NULL) goto err1;
           if (pfi->dnum[3] < 1)
             {
-              sprintf(pout, "Warning: Invalid TDEF syntax in %s -- Changing size of T axis from %d to 1 \n", pfi->dnam,
-                      pfi->dnum[3]);
+              snprintf(pout, sizeof(pout), "Warning: Invalid TDEF syntax in %s -- Changing size of T axis from %d to 1 \n",
+                       pfi->dnam, pfi->dnum[3]);
               gaprnt(1, pout);
               pfi->dnum[3] = 1;
             }
@@ -2006,7 +2069,7 @@ read_gradsdes(char *filename, dsets_t *pfi)
               if (fgets(rec, 512, descr) == NULL)
                 {
                   gaprnt(0, "Open Error:  Unexpected EOF reading variables\n");
-                  sprintf(pout, "Was expecting %i records.  Found %i.\n", pfi->vnum, i);
+                  snprintf(pout, sizeof(pout), "Was expecting %i records.  Found %i.\n", pfi->vnum, i);
                   gaprnt(2, pout);
                   goto retrn;
                 }
@@ -2045,7 +2108,7 @@ read_gradsdes(char *filename, dsets_t *pfi)
               if (cmpwrd("endvars", rec))
                 {
                   gaprnt(0, "Open Error:  Unexpected ENDVARS record\n");
-                  sprintf(pout, "Was expecting %i records.  Found %i.\n", pfi->vnum, i);
+                  snprintf(pout, sizeof(pout), "Was expecting %i records.  Found %i.\n", pfi->vnum, i);
                   gaprnt(2, pout);
                   goto err9;
                 }
@@ -2201,7 +2264,7 @@ read_gradsdes(char *filename, dsets_t *pfi)
                 }
               else
                 {
-                  sprintf(pout, "Open Error:  Looking for \"endvars\", found \"%s\" instead.\n", rec);
+                  snprintf(pout, sizeof(pout), "Open Error:  Looking for \"endvars\", found \"%s\" instead.\n", rec);
                   gaprnt(0, pout);
                   goto err9;
                 }
@@ -2274,7 +2337,7 @@ read_gradsdes(char *filename, dsets_t *pfi)
           goto err8;
         }
       pfi->ens1 = ens;
-      sprintf(ens->name, "1");
+      snprintf(ens->name, sizeof(ens->name), "1");
       ens->length = pfi->dnum[3];
       ens->gt = 1;
       gr2t(pfi->grvals[3], 1, &ens->tinit);
@@ -2510,7 +2573,7 @@ read_gradsdes(char *filename, dsets_t *pfi)
           ch = gafndt(pfi->name, &tdefe, &tdefe, pfi->abvals[3], pfi->pchsub1, pfi->ens1, ens->gt, e, &flag);
           if (ch == NULL)
             {
-              sprintf(pout, "Open Error: couldn't determine data file name for e=%d t=%d\n", e, ens->gt);
+              snprintf(pout, sizeof(pout), "Open Error: couldn't determine data file name for e=%d t=%d\n", e, ens->gt);
               gaprnt(0, pout);
               goto err8;
             }
@@ -2562,7 +2625,7 @@ read_gradsdes(char *filename, dsets_t *pfi)
               pos = gafndt(pfi->name, &tdef, &tdefe, pfi->abvals[3], pfi->pchsub1, pfi->ens1, t, e, &flag);
               if (pos == NULL)
                 {
-                  sprintf(pout, "Open Error: couldn't determine data file name for e=%d t=%d\n", e, t);
+                  snprintf(pout, sizeof(pout), "Open Error: couldn't determine data file name for e=%d t=%d\n", e, t);
                   gaprnt(0, pout);
                   goto err8;
                 }
diff --git a/src/lib/healpix/CMakeLists.txt b/src/lib/healpix/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1bdadb5eec784b5747d5518633c1448b256c3e15
--- /dev/null
+++ b/src/lib/healpix/CMakeLists.txt
@@ -0,0 +1,32 @@
+list( APPEND healpix_src_files
+                     an-bool.h
+                     bl-nl.h
+                     bl-nl.inc
+                     bl-nl.ph
+                     bl.c
+                     bl.h
+                     bl.inc
+                     bl.ph
+                     healpix-utils.c
+                     healpix-utils.h
+                     healpix.c
+                     healpix.h
+                     keywords.h
+                     mathutil.c
+                     mathutil.h
+                     mathutil.inc
+                     os-features.h
+                     permutedsort.c
+                     permutedsort.h
+                     qsort_reentrant.c
+                     starutil.c
+                     starutil.h
+                     starutil.inc
+                     interpolation.c
+                     interpolation.h
+                     stdint_msc.h
+)
+
+add_library(healpix
+  ${healpix_src_files}
+)
diff --git a/src/lib/yac/CMakeLists.txt b/src/lib/yac/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7d373f3c7899feb33df08023c1208da3aee5e0ac
--- /dev/null
+++ b/src/lib/yac/CMakeLists.txt
@@ -0,0 +1,35 @@
+list( APPEND yac_src_files
+               area.c
+               area.h
+               basic_grid.h
+               basic_grid_data.h
+               bnd_circle.c
+               check_overlap.c
+               clipping.c
+               clipping.h
+               ensure_array_size.c
+               ensure_array_size.h
+               field_data.h
+               geometry.h
+               grid.h
+               grid_cell.c
+               grid_cell.h
+               intersection.c
+               interval_tree.c
+               interval_tree.h
+               location.h
+               sphere_part.c
+               sphere_part.h
+               utils_core.c
+               utils_core.h
+               utils_common.h
+               yac_ld_interface.h
+               yac_types.h
+               yac_version.h
+)
+
+add_library(yac
+  ${yac_src_files}
+)
+
+target_compile_definitions(yac PUBLIC YAC_FOR_CDO)
diff --git a/src/lib/yac/Makefile.am b/src/lib/yac/Makefile.am
index 4eef78764dcc9733153b8ee2e4ed335fe239cdc8..d4c28558db79313dd05525e81c47884444608a25 100644
--- a/src/lib/yac/Makefile.am
+++ b/src/lib/yac/Makefile.am
@@ -4,24 +4,29 @@ noinst_LTLIBRARIES = libyac.la
 libyac_la_SOURCES =                 \
                area.c               \
                area.h               \
+               basic_grid.h         \
+               basic_grid_data.h    \
                bnd_circle.c         \
                check_overlap.c      \
                clipping.c           \
                clipping.h           \
                ensure_array_size.c  \
                ensure_array_size.h  \
+               field_data.h         \
                geometry.h           \
-               grid.h               \
                grid_cell.c          \
                grid_cell.h          \
                intersection.c       \
                interval_tree.c      \
                interval_tree.h      \
+               location.h           \
                sphere_part.c        \
                sphere_part.h        \
-               utils.c              \
-               utils.h              \
+               utils_core.c         \
+               utils_core.h         \
+               utils_common.h       \
                yac_ld_interface.h   \
+               yac_types.h          \
                yac_version.h
 #
 CPPFLAGS += -DYAC_FOR_CDO
diff --git a/src/lib/yac/area.c b/src/lib/yac/area.c
index be414c6568b4d3f461b12905be80aae338cefbe8..367a574da15ddd9927717d65a4293660ab98f808 100644
--- a/src/lib/yac/area.c
+++ b/src/lib/yac/area.c
@@ -1,48 +1,6 @@
-/**
- * @file area.c
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -50,10 +8,10 @@
 #include <float.h>
 
 #include "area.h"
-#include "grid.h"
+#include "basic_grid.h"
 #include "clipping.h"
 #include "geometry.h"
-#include "utils.h"
+#include "utils_core.h"
 #include "ensure_array_size.h"
 
 static inline double scalar_product(double a[], double b[]);
@@ -83,9 +41,9 @@ double yac_triangle_area ( struct grid_cell cell ) {
 
   /* First, compute cross products Uij = Vi x Vj. */
 
-  crossproduct_ld(triangle[0], triangle[1], u01);
-  crossproduct_ld(triangle[1], triangle[2], u12);
-  crossproduct_ld(triangle[2], triangle[0], u20);
+  crossproduct_kahan(triangle[0], triangle[1], u01);
+  crossproduct_kahan(triangle[1], triangle[2], u12);
+  crossproduct_kahan(triangle[2], triangle[0], u20);
 
   /*  Normalize Uij to unit vectors. */
 
@@ -237,7 +195,7 @@ static double tri_area(double u[3], double v[3], double w[3]) {
 
 static inline int compute_norm_vector(double a[], double b[], double norm[]) {
 
-  crossproduct_ld(a, b, norm);
+  crossproduct_kahan(a, b, norm);
 
   double scale = sqrt(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]);
 
@@ -500,7 +458,7 @@ static double tri_area_info(
   double cross[3][3];
   struct sin_cos_angle angles[3];
   for (int i = 0, j = 2; i < 3; j = i++) {
-    crossproduct_ld(corners[j], corners[i], cross[i]);
+    crossproduct_kahan(corners[j], corners[i], cross[i]);
     angles[i] =
       sin_cos_angle_new(sqrt(cross[i][0]*cross[i][0] +
                              cross[i][1]*cross[i][1] +
@@ -510,8 +468,6 @@ static double tri_area_info(
 
   double area = tri_area_(angles[0], angles[1], angles[2]);
 
-  if (area < 1e-10) return 0.0;
-
   // the barycenter of the triangle is given by the sum edge norm vector
   // scaled by half of the associated edge length
   for (int i = 0; i < 3; ++i) {
diff --git a/src/lib/yac/area.h b/src/lib/yac/area.h
index aa134f03cd8fd5f27849d08152c11b2ed7b5a728..385bccae9a0e22744fe1bb50b394ecb7ec06c298 100644
--- a/src/lib/yac/area.h
+++ b/src/lib/yac/area.h
@@ -1,50 +1,11 @@
-/**
- * @file area.h
- *
- * @copyright Copyright  (C)  2013 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifndef AREA_H
 #define AREA_H
 
-#include "grid.h"
+#include "basic_grid.h"
 #include "clipping.h"
 
 /** \example test_area.c
diff --git a/src/lib/yac/basic_grid.h b/src/lib/yac/basic_grid.h
new file mode 100644
index 0000000000000000000000000000000000000000..e68b63d3b9e36dddcbbc9c5d21c707e6b6f81d45
--- /dev/null
+++ b/src/lib/yac/basic_grid.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef BASIC_GRID_H
+#define BASIC_GRID_H
+
+#include "basic_grid_data.h"
+#include "location.h"
+#include "field_data.h"
+
+/** \example test_grid.c
+ */
+
+struct yac_interp_field {
+  enum yac_location location;
+  size_t coordinates_idx;
+  size_t masks_idx;
+};
+
+struct yac_basic_grid;
+
+struct yac_basic_grid * yac_basic_grid_new(
+  char const * name, struct yac_basic_grid_data grid_data);
+struct yac_basic_grid * yac_basic_grid_empty_new(char const * name);
+yac_const_coordinate_pointer yac_basic_grid_get_field_coordinates(
+  struct yac_basic_grid * grid, struct yac_interp_field field);
+int const * yac_basic_grid_get_field_mask(
+  struct yac_basic_grid * grid, struct yac_interp_field field);
+int const * yac_basic_grid_get_core_mask(
+  struct yac_basic_grid * grid, enum yac_location location);
+char const * yac_basic_grid_get_name(struct yac_basic_grid * grid);
+struct yac_basic_grid_data * yac_basic_grid_get_data(
+  struct yac_basic_grid * grid);
+struct yac_field_data * yac_basic_grid_get_field_data(
+  struct yac_basic_grid * grid, enum yac_location location);
+size_t yac_basic_grid_get_data_size(
+  struct yac_basic_grid * grid, enum yac_location location);
+size_t yac_basic_grid_get_named_mask_idx(
+  struct yac_basic_grid * grid, enum yac_location location,
+  char const * mask_name);
+size_t yac_basic_grid_add_coordinates(
+  struct yac_basic_grid * grid, enum yac_location location,
+  yac_coordinate_pointer coordinates, size_t count);
+size_t yac_basic_grid_add_coordinates_nocpy(
+  struct yac_basic_grid * grid, enum yac_location location,
+  yac_coordinate_pointer coordinates);
+size_t yac_basic_grid_add_mask(
+  struct yac_basic_grid * grid, enum yac_location location,
+  int const * mask, size_t count, char const * mask_name);
+size_t yac_basic_grid_add_mask_nocpy(
+  struct yac_basic_grid * grid, enum yac_location location,
+  int const * mask, char const * mask_name);
+void yac_basic_grid_delete(struct yac_basic_grid * grid);
+
+struct yac_basic_grid * yac_basic_grid_reg_2d_new(
+  char const * name, size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid * yac_basic_grid_reg_2d_deg_new(
+  char const * name, size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid * yac_basic_grid_curve_2d_new(
+  char const * name, size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid * yac_basic_grid_curve_2d_deg_new(
+  char const * name, size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid * yac_basic_grid_unstruct_new(
+  char const * name, size_t nbr_vertices, size_t nbr_cells,
+  int *num_vertices_per_cell, double *x_vertices, double *y_vertices,
+  int *cell_to_vertex);
+
+struct yac_basic_grid * yac_basic_grid_unstruct_deg_new(
+  char const * name, size_t nbr_vertices, size_t nbr_cells,
+  int *num_vertices_per_cell, double *x_vertices, double *y_vertices,
+  int *cell_to_vertex);
+
+struct yac_basic_grid * yac_basic_grid_unstruct_ll_new(
+  char const * name, size_t nbr_vertices, size_t nbr_cells,
+  int *num_vertices_per_cell, double *x_vertices, double *y_vertices,
+  int *cell_to_vertex);
+
+struct yac_basic_grid * yac_basic_grid_unstruct_ll_deg_new(
+  char const * name, size_t nbr_vertices, size_t nbr_cells,
+  int *num_vertices_per_cell, double *x_vertices, double *y_vertices,
+  int *cell_to_vertex);
+
+#endif // BASIC_GRID_H
diff --git a/src/lib/yac/basic_grid_data.h b/src/lib/yac/basic_grid_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..992c2d24982993241a7adaf8d9e07ce5338e5bef
--- /dev/null
+++ b/src/lib/yac/basic_grid_data.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef BASIC_GRID_DATA_H
+#define BASIC_GRID_DATA_H
+
+#include "yac_types.h"
+
+struct yac_basic_grid_data {
+  yac_coordinate_pointer vertex_coordinates;
+  yac_int * cell_ids;
+  yac_int * vertex_ids;
+  yac_int * edge_ids;
+  size_t num_cells; // number of local cells (owned by local process)
+  size_t num_vertices; // number of local vertices (owned by local process)
+  size_t num_edges; // number of local edges (owned by local process)
+  int * core_cell_mask;
+  int * core_vertex_mask;
+  int * core_edge_mask;
+  int * num_vertices_per_cell;
+  int * num_cells_per_vertex;
+  size_t * cell_to_vertex;
+  size_t * cell_to_vertex_offsets;
+  size_t * cell_to_edge;
+  size_t * cell_to_edge_offsets;
+  size_t * vertex_to_cell;
+  size_t * vertex_to_cell_offsets;
+  yac_size_t_2_pointer edge_to_vertex;
+  enum yac_edge_type * edge_type;
+  size_t num_total_cells; // number of locally stored cells
+  size_t num_total_vertices; // number of locally stored vertices
+  size_t num_total_edges; // number of locally stored edges
+};
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_reg_2d(
+  size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_reg_2d_deg(
+  size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_curve_2d(
+  size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_curve_2d_deg(
+  size_t nbr_vertices[2], int cyclic[2],
+  double *lon_vertices, double *lat_vertices);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_unstruct(
+  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
+  double *x_vertices, double *y_vertices, int *cell_to_vertex);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_unstruct_deg(
+  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
+  double *x_vertices, double *y_vertices, int *cell_to_vertex);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_unstruct_ll(
+  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
+  double *x_vertices, double *y_vertices, int *cell_to_vertex);
+
+struct yac_basic_grid_data yac_generate_basic_grid_data_unstruct_ll_deg(
+  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
+  double *x_vertices, double *y_vertices, int *cell_to_vertex);
+
+void yac_basic_grid_data_free(struct yac_basic_grid_data grid);
+
+#endif // BASIC_GRID_DATA_H
diff --git a/src/lib/yac/bnd_circle.c b/src/lib/yac/bnd_circle.c
index cb21b62a6c11bb74ec21d98cfef5665c9dee831a..d464d5330d76308616772634e39d76d970a2eed8 100644
--- a/src/lib/yac/bnd_circle.c
+++ b/src/lib/yac/bnd_circle.c
@@ -1,48 +1,6 @@
-/**
- * @file bnd_circle.c
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifdef HAVE_CONFIG_H
 // Get the definition of the 'restrict' keyword.
@@ -55,8 +13,8 @@
 #include <float.h>
 
 #include "geometry.h"
-#include "utils.h"
-#include "grid.h"
+#include "utils_core.h"
+#include "basic_grid.h"
 #include "ensure_array_size.h"
 
 /** \file bnd_circle.c
@@ -92,7 +50,7 @@ void yac_get_cell_circumscribe_circle_unstruct_triangle(
           ac[3] = {b[0]-c[0], b[1]-c[1], b[2]-c[2]};
 
    // it is assumed that the angles of a triangle do not get too small...
-   // crossproduct_ld(ab, ac, bnd_circle->base_vector);
+   // crossproduct_kahan(ab, ac, bnd_circle->base_vector);
    crossproduct_d(ab, ac, bnd_circle->base_vector);
    normalise_vector(bnd_circle->base_vector);
 
@@ -123,7 +81,7 @@ static inline double get_sin_vector_angle(
   double a[3], double b[3]) {
 
   double cross_ab[3];
-  crossproduct_ld(a, b, cross_ab);
+  crossproduct_kahan(a, b, cross_ab);
 
   return sqrt(cross_ab[0]*cross_ab[0] +
               cross_ab[1]*cross_ab[1] +
diff --git a/src/lib/yac/cdo_remap.cc b/src/lib/yac/cdo_remap.cc
index 1902a5c8abc146c8edb5141b0660a27772b5ffe6..323e206bc7e1612d7eea94e10fe336d62bcd80af 100644
--- a/src/lib/yac/cdo_remap.cc
+++ b/src/lib/yac/cdo_remap.cc
@@ -9,13 +9,13 @@ static void
 compute_testfield(Varray<double> &array, const Varray<double> &cellCentersLon, const Varray<double> &cellCentersLat)
 {
   double xyz[3];
-  const auto numCells = array.size();
+  auto numCells = array.size();
   for (size_t i = 0; i < numCells; ++i)
     {
       LL_to_XYZ(cellCentersLon[i], cellCentersLat[i], xyz);
-      const auto x = xyz[0];
-      const auto y = xyz[1];
-      const auto z = xyz[2];
+      auto x = xyz[0];
+      auto y = xyz[1];
+      auto z = xyz[2];
       array[i] = 1.0 + std::pow(x, 8.0) + std::exp(2.0 * y * y * y) + std::exp(2.0 * x * x) + 10.0 * x * y * z;
     }
 }
@@ -23,7 +23,7 @@ compute_testfield(Varray<double> &array, const Varray<double> &cellCentersLon, c
 static
 void output_ext(const Varray<double> &array)
 {
-  const auto numCells = array.size();
+  auto numCells = array.size();
   fprintf(stdout, "%8d %4d %8g %8zu\n", 010101, 1, 0.0, numCells);
 
   int nout = 0;
@@ -48,7 +48,7 @@ void grid_init_regular(GridInfo &grid, double increment)
 
 int main(void)
 {
-  const double missval = -9.e33;
+  double missval = -9.e33;
   RemapSearch remapSearch;
 
   auto &srcGrid = remapSearch.srcGrid;
@@ -67,7 +67,7 @@ int main(void)
   remap_conserv(NormOpt::FRACAREA, remapSearch, srcArray, tgtArray, missval);
 
   //output_ext(srcArray);
-  //output_ext(tgtArray);
+  output_ext(tgtArray);
 
   gridcell_search_delete(remapSearch.gcs);
 
diff --git a/src/lib/yac/cdo_remap_conserv.cc b/src/lib/yac/cdo_remap_conserv.cc
index 36d32011f8aa8598b4197115b7435c1a023ebd2e..7285a1825b8275715db771c018bcaa19fb507877 100644
--- a/src/lib/yac/cdo_remap_conserv.cc
+++ b/src/lib/yac/cdo_remap_conserv.cc
@@ -1,10 +1,9 @@
-#include <algorithm> // std::sort
-#include <numeric>   // std::accumulate
+#include <algorithm>  // std::sort
+#include <numeric>    // std::accumulate
 
 #include "cdo_remap.h"
 
-
-const auto is_not_equal = [](auto a, auto b) noexcept { return  (a < b || b < a); };
+const auto is_not_equal = [](auto a, auto b) noexcept { return (a < b || b < a); };
 const auto is_equal = [](auto a, auto b) noexcept { return !(a < b || b < a); };
 
 extern "C"
@@ -88,17 +87,14 @@ cdo_compute_overlap_areas(size_t numSearchCells, CellSearch &search, const GridC
 }
 
 static double
-get_edge_direction(double * ref_corner, double * corner_a, double * corner_b)
+get_edge_direction(const double *ref_corner, double *corner_a, double *corner_b)
 {
   double edge_norm[3];
-  crossproduct_ld(corner_a, corner_b, edge_norm);
+  crossproduct_kahan(corner_a, corner_b, edge_norm);
   normalise_vector(edge_norm);
 
   // sine of the angle between the edge and the reference corner
-  double angle =
-    edge_norm[0] * ref_corner[0] +
-    edge_norm[1] * ref_corner[1] +
-    edge_norm[2] * ref_corner[2];
+  double angle = edge_norm[0] * ref_corner[0] + edge_norm[1] * ref_corner[1] + edge_norm[2] * ref_corner[2];
 
   // if the reference corner is directly on the edge
   // (for small angles sin(x)==x)
@@ -110,7 +106,7 @@ get_edge_direction(double * ref_corner, double * corner_a, double * corner_b)
 static void
 cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, const GridCell &gridCell)
 {
-  const auto targetCell = gridCell.yacGridCell;
+  auto targetCell = gridCell.yacGridCell;
   auto &overlapAreas = search.partialAreas;
   auto &overlapCells = search.overlapCells;
   auto &sourceCells = search.gridCells;
@@ -121,9 +117,7 @@ cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, con
   // the triangulation algorithm only works for cells that only have great circle edges
   enum yac_edge_type edgeTypes[3] = { GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE };
   double coordinates_xyz[3][3] = { { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 } };
-  coordinates_xyz[0][0] = baseCorner[0];
-  coordinates_xyz[0][1] = baseCorner[1];
-  coordinates_xyz[0][2] = baseCorner[2];
+  for (int k = 0; k < 3; ++k) coordinates_xyz[0][k] = baseCorner[k];
 
   // data structure to hold the triangles of the target cell
   struct grid_cell partialCell;
@@ -142,20 +136,16 @@ cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, con
   //  first corner, therefore we can skip them)
   for (size_t cornerIdx = 1; cornerIdx < targetCell.num_corners - 1; ++cornerIdx)
     {
-      const auto cornerA = targetCell.coordinates_xyz[cornerIdx];
-      const auto cornerB = targetCell.coordinates_xyz[(cornerIdx + 1)];
+      auto cornerA = targetCell.coordinates_xyz[cornerIdx];
+      auto cornerB = targetCell.coordinates_xyz[(cornerIdx + 1)];
 
       // if the current edge has a length of zero
       if (points_are_identically(cornerA, cornerB)) continue;
 
-      const auto edgeDirection = get_edge_direction(baseCorner, cornerA, cornerB);
+      auto edgeDirection = get_edge_direction(baseCorner, cornerA, cornerB);
 
-      partialCell.coordinates_xyz[1][0] = cornerA[0];
-      partialCell.coordinates_xyz[1][1] = cornerA[1];
-      partialCell.coordinates_xyz[1][2] = cornerA[2];
-      partialCell.coordinates_xyz[2][0] = cornerB[0];
-      partialCell.coordinates_xyz[2][1] = cornerB[1];
-      partialCell.coordinates_xyz[2][2] = cornerB[2];
+      for (int k = 0; k < 3; ++k) partialCell.coordinates_xyz[1][k] = cornerA[k];
+      for (int k = 0; k < 3; ++k) partialCell.coordinates_xyz[2][k] = cornerB[k];
 
       // clip the current target cell triangle with all source cells
       yac_cell_clipping(numSearchCells, sourceCells.data(), partialCell, overlapCells.data());
@@ -173,7 +163,7 @@ cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, con
     {
       if (overlapAreas[i] < 0.0) overlapAreas[i] = -overlapAreas[i];
     }
- 
+
 #ifdef VERBOSE
   for (size_t i = 0; i < numSearchCells; i++) printf("overlap area %zu: %lf\n", i, overlapAreas[i]);
 #endif
@@ -183,7 +173,7 @@ static void
 set_cell_coordinates_yac(const size_t cellIndex, const size_t numCorners, const GridInfo &gridInfo,
                          const struct grid_cell &yacGridCell, double *x, double *y)
 {
-  const auto storeXY = (x && y);
+  auto storeXY = (x && y);
   auto xyz = yacGridCell.coordinates_xyz;
 
   const auto cellCornersLon = &gridInfo.cellCornersLon[cellIndex * numCorners];
@@ -207,24 +197,23 @@ set_cell_coordinates(const size_t cellIndex, const size_t numCorners, const Grid
 }
 
 static void
-set_coordinates_yac(const size_t numCells, const Varray<size_t> &cellIndices,
-                    const size_t numCorners, const GridInfo &gridInfo, const Varray<grid_cell> &yacGridCell)
+set_coordinates_yac(const size_t numCells, const Varray<size_t> &cellIndices, const size_t numCorners, const GridInfo &gridInfo,
+                    const Varray<grid_cell> &yacGridCell)
 {
   for (size_t i = 0; i < numCells; ++i)
     set_cell_coordinates_yac(cellIndices[i], numCorners, gridInfo, yacGridCell[i], nullptr, nullptr);
 }
 
 static void
-set_coordinates_yac(const double (*xyzCoords)[3], const size_t numCells, const Varray<size_t> &cellIndices,
-                    const size_t numCorners, const Varray<grid_cell> &yacGridCell)
+set_coordinates_yac(const double (*xyzCoords)[3], const size_t numCells, const Varray<size_t> &cellIndices, const size_t numCorners,
+                    const Varray<grid_cell> &yacGridCell)
 {
   for (size_t i = 0; i < numCells; ++i)
     {
-      const auto offset = cellIndices[i] * numCorners;
+      auto offset = cellIndices[i] * numCorners;
       auto xyz = yacGridCell[i].coordinates_xyz;
       for (size_t k = 0; k < numCorners; ++k)
-        for (size_t l = 0; l < 3; ++l)
-          xyz[k][l] = xyzCoords[offset + k][l];
+        for (size_t l = 0; l < 3; ++l) xyz[k][l] = xyzCoords[offset + k][l];
     }
 }
 
@@ -270,7 +259,7 @@ remove_invalid_weights(size_t gridSize, size_t numWeights, Varray<double> &weigh
   size_t n = 0;
   for (size_t i = 0; i < numWeights; ++i)
     {
-      const auto cellIndex = (weights[i] > 0.0) ? searchIndices[i] : gridSize;
+      auto cellIndex = (weights[i] > 0.0) ? searchIndices[i] : gridSize;
       if (cellIndex != gridSize)
         {
           weights[n] = weights[i];
@@ -283,13 +272,12 @@ remove_invalid_weights(size_t gridSize, size_t numWeights, Varray<double> &weigh
 }
 
 static size_t
-remove_unmask_weights(const Varray<short> &gridMask, size_t numWeights, Varray<double> &weights,
-                      Varray<size_t> &searchIndices)
+remove_unmask_weights(const Varray<short> &gridMask, size_t numWeights, Varray<double> &weights, Varray<size_t> &searchIndices)
 {
   size_t n = 0;
   for (size_t i = 0; i < numWeights; ++i)
     {
-      const auto cellIndex = searchIndices[i];
+      auto cellIndex = searchIndices[i];
       /*
         Store the appropriate addresses and weights.
         Also add contributions to cell areas.
@@ -311,17 +299,15 @@ normalize_weights(NormOpt normOpt, double cellArea, double cellFrac, size_t numW
 {
   if (normOpt == NormOpt::DESTAREA)
     {
-      const auto normFactor = is_not_equal(cellArea, 0.0) ? 1.0 / cellArea : 0.0;
+      auto normFactor = is_not_equal(cellArea, 0.0) ? 1.0 / cellArea : 0.0;
       for (size_t i = 0; i < numWeights; ++i) weights[i] *= normFactor;
     }
   else if (normOpt == NormOpt::FRACAREA)
     {
-      const auto normFactor = is_not_equal(cellFrac, 0.0) ? 1.0 / cellFrac : 0.0;
+      auto normFactor = is_not_equal(cellFrac, 0.0) ? 1.0 / cellFrac : 0.0;
       for (size_t i = 0; i < numWeights; ++i) weights[i] *= normFactor;
     }
-  else if (normOpt == NormOpt::NONE)
-    {
-    }
+  else if (normOpt == NormOpt::NONE) {}
 }
 
 static void
@@ -379,7 +365,7 @@ get_lonlat_circle_index(size_t numCells, size_t numCorners, const Varray<double>
 
       for (size_t i = 0; i < numCells; i += iadd)
         {
-          const auto i4 = i * 4;
+          auto i4 = i * 4;
           num_i++;
           // clang-format off
           if (is_equal(clon[i4 + 1], clon[i4 + 2]) && is_equal(clon[i4 + 3], clon[i4 + 0]) &&
@@ -420,7 +406,8 @@ conserv_remap(const Varray<double> &srcArray, size_t numWeights, const Varray<do
 }
 
 void
-remap_conserv(NormOpt normOpt, const RemapSearch &remapSearch, const Varray<double> &srcArray, Varray<double> &tgtArray, double missval)
+remap_conserv(NormOpt normOpt, const RemapSearch &remapSearch, const Varray<double> &srcArray, Varray<double> &tgtArray,
+              double missval)
 {
   auto &srcGrid = remapSearch.srcGrid;
   auto &tgtGrid = remapSearch.tgtGrid;
@@ -444,13 +431,13 @@ remap_conserv(NormOpt normOpt, const RemapSearch &remapSearch, const Varray<doub
 
   if (srcNumCorners == 4)
     {
-      const auto lonlatCircleIndex = get_lonlat_circle_index(srcGrid);
+      auto lonlatCircleIndex = get_lonlat_circle_index(srcGrid);
       if (lonlatCircleIndex >= 0) srcEdgeType = &lonlatCircleType[lonlatCircleIndex];
     }
 
   if (tgtNumCorners == 4)
     {
-      const auto lonlatCircleIndex = get_lonlat_circle_index(tgtGrid);
+      auto lonlatCircleIndex = get_lonlat_circle_index(tgtGrid);
       if (lonlatCircleIndex >= 0)
         {
           tgtCellType = LON_LAT_CELL;
@@ -474,7 +461,7 @@ remap_conserv(NormOpt normOpt, const RemapSearch &remapSearch, const Varray<doub
     {
       tgtArray[tgtCellIndex] = missval;
 
-      //if (!tgtGrid.mask[tgtCellIndex]) continue;
+      // if (!tgtGrid.mask[tgtCellIndex]) continue;
 
       set_cell_coordinates(tgtCellIndex, tgtNumCorners, tgtGrid, tgtGridCell);
 
@@ -501,14 +488,14 @@ remap_conserv(NormOpt normOpt, const RemapSearch &remapSearch, const Varray<doub
 
       auto numWeights = remove_invalid_areas(numSearchCells, partialWeights, searchIndices);
 
-      const auto tgtCellArea = gridcell_area(tgtGridCell.yacGridCell);
+      auto tgtCellArea = gridcell_area(tgtGridCell.yacGridCell);
 
       if (normOpt == NormOpt::FRACAREA) correct_weights(tgtCellArea, numWeights, partialWeights);
 
       numWeights = remove_invalid_weights(srcNumCells, numWeights, partialWeights, searchIndices);
       numWeights = remove_unmask_weights(srcGridMask, numWeights, partialWeights, searchIndices);
 
-      const auto tgtCellFrac = std::accumulate(partialWeights.begin(), partialWeights.begin() + numWeights, 0.0);
+      auto tgtCellFrac = std::accumulate(partialWeights.begin(), partialWeights.begin() + numWeights, 0.0);
 
       if (numWeights)
         {
diff --git a/src/lib/yac/cdo_remap_conserv2.cc b/src/lib/yac/cdo_remap_conserv2.cc
new file mode 100644
index 0000000000000000000000000000000000000000..40430323fdd71adeac433b203e157e77c27a4ff6
--- /dev/null
+++ b/src/lib/yac/cdo_remap_conserv2.cc
@@ -0,0 +1,728 @@
+#include <algorithm>  // std::sort
+#include <numeric>    // std::accumulate
+
+#include "cdo_remap.h"
+
+const auto is_not_equal = [](auto a, auto b) noexcept { return (a < b || b < a); };
+const auto is_equal = [](auto a, auto b) noexcept { return !(a < b || b < a); };
+
+extern "C"
+{
+#include "clipping.h"
+#include "area.h"
+#include "geometry.h"
+#include "yac_interp_method_conserv.h"
+}
+
+struct CellSearch
+{
+  enum yac_edge_type *edgeType = nullptr;
+  size_t numCellCorners = 0;
+  size_t maxCells = 0;
+  Varray<double> partialAreas;
+  Varray<struct grid_cell> gridCells;
+  Varray<struct grid_cell> overlapCells;
+};
+
+static void
+cellsearch_realloc(size_t numCells, CellSearch &search)
+{
+  if (numCells > search.maxCells)
+    {
+      search.partialAreas.resize(numCells);
+      search.overlapCells.resize(numCells);
+      search.gridCells.resize(numCells);
+
+      for (size_t i = search.maxCells; i < numCells; ++i)
+        {
+          search.overlapCells[i].array_size = 0;
+          search.overlapCells[i].num_corners = 0;
+          search.overlapCells[i].edge_type = nullptr;
+          search.overlapCells[i].coordinates_xyz = nullptr;
+
+          search.gridCells[i].array_size = search.numCellCorners;
+          search.gridCells[i].num_corners = search.numCellCorners;
+          search.gridCells[i].edge_type = search.edgeType;
+          search.gridCells[i].coordinates_xyz = new double[search.numCellCorners][3];
+        }
+
+      search.maxCells = numCells;
+    }
+}
+
+static void
+cellsearch_free(CellSearch &search)
+{
+  for (size_t i = 0; i < search.maxCells; i++)
+    {
+      if (search.overlapCells[i].array_size > 0)
+        {
+          if (search.overlapCells[i].coordinates_xyz) free(search.overlapCells[i].coordinates_xyz);
+          if (search.overlapCells[i].edge_type) free(search.overlapCells[i].edge_type);
+        }
+
+      delete[] search.gridCells[i].coordinates_xyz;
+    }
+}
+
+static enum yac_cell_type
+get_cell_type(struct grid_cell target_cell)
+{
+  /*
+  YAC_ASSERT(
+    target_cell.num_corners > 0, "ERROR(get_cell_type): CELL without edge")
+  */
+  enum yac_cell_type cell_type = MIXED_CELL;
+
+  // if the cell is a typical lon-lat cell
+  if ((target_cell.num_corners == 4)
+      && ((target_cell.edge_type[0] == LAT_CIRCLE_EDGE && target_cell.edge_type[1] == LON_CIRCLE_EDGE
+           && target_cell.edge_type[2] == LAT_CIRCLE_EDGE && target_cell.edge_type[3] == LON_CIRCLE_EDGE)
+          || (target_cell.edge_type[0] == LON_CIRCLE_EDGE && target_cell.edge_type[1] == LAT_CIRCLE_EDGE
+              && target_cell.edge_type[2] == LON_CIRCLE_EDGE && target_cell.edge_type[3] == LAT_CIRCLE_EDGE)))
+    {
+
+      cell_type = LON_LAT_CELL;
+    }
+  else
+    {
+
+      size_t count_lat_edges = 0, count_great_circle_edges = 0;
+
+      // count the number of edges for each type
+      // (lon edges are counted as gc ones)
+      for (size_t i = 0; i < target_cell.num_corners; ++i)
+        if (target_cell.edge_type[i] == LON_CIRCLE_EDGE || target_cell.edge_type[i] == GREAT_CIRCLE_EDGE)
+          count_great_circle_edges++;
+        else
+          count_lat_edges++;
+
+      // if the cell is a lon lat cell with one lat edge having a length of zero
+      // due to being at a pole
+      if ((count_lat_edges == 1) && (count_great_circle_edges == 2))
+        {
+
+          size_t i;
+          for (i = 0; i < 3; ++i)
+            if (target_cell.edge_type[i] == LAT_CIRCLE_EDGE) break;
+          size_t pol_index = (3 + i - 1) % 3;
+          if (fabs(fabs(target_cell.coordinates_xyz[pol_index][2]) - 1.0) < yac_angle_low_tol) cell_type = LON_LAT_CELL;
+
+          // if the cell only consists of great circle edges
+        }
+      else if (count_lat_edges == 0)
+        cell_type = GREAT_CIRCLE_CELL;
+
+      // if the cell only consists of lat circle edges
+      else if (count_great_circle_edges == 0)
+        cell_type = LAT_CELL;
+    }
+  /*
+  YAC_ASSERT(
+    cell_type != MIXED_CELL,
+    "invalid cell type (cell contains edges consisting "
+    "of great circles and circles of latitude)")
+  */
+  return cell_type;
+}
+
+static inline double
+gridcell_area(const grid_cell &cell)
+{
+  return yac_huiliers_area(cell);
+}
+
+static void
+cdo_compute_overlap_areas(size_t numSearchCells, CellSearch &search, const GridCell &gridCell)
+{
+  auto &overlapCells = search.overlapCells;
+
+  // Do the clipping and get the cell for the overlapping area
+  yac_cell_clipping(numSearchCells, search.gridCells.data(), gridCell.yacGridCell, overlapCells.data());
+
+  // Get the partial areas for the overlapping regions
+  for (size_t i = 0; i < numSearchCells; i++) search.partialAreas[i] = gridcell_area(overlapCells[i]);
+
+#ifdef VERBOSE
+  for (size_t i = 0; i < numSearchCells; i++) printf("overlap area : %lf\n", search.partialAreas[i]);
+#endif
+}
+/*
+static void
+cdo_compute_concave_overlap_info(size_t numSearchCells, CellSearch &search, const GridCell &gridCell, double tgtLon, double tgtLat,
+                                 double *overlapAreas, double (*overlapBarycenters)[3])
+{
+  auto &overlapCells = search.overlapCells;
+  auto &sourceCells = search.gridCells;
+  auto targetCell = gridCell.yacGridCell;
+
+  if (overlapBarycenters != NULL)
+    for (size_t i = 0; i < numSearchCells; ++i)
+      for (int j = 0; j < 3; ++j) overlapBarycenters[i][j] = 0.0;
+
+  auto target_cell_type = (targetCell.num_corners > 3) ? get_cell_type(targetCell) : MIXED_CELL;
+
+  if (targetCell.num_corners < 4 || target_cell_type == LON_LAT_CELL)
+    {
+      yac_cell_clipping(numSearchCells, sourceCells.data(), targetCell, overlapCells.data());
+      for (size_t i = 0; i < numSearchCells; ++i)
+        {
+          if (overlapCells[i].num_corners > 1)
+            {
+              if (overlapBarycenters == NULL)
+                overlapAreas[i] = yac_huiliers_area(overlapCells[i]);
+              else
+                {
+                  overlapAreas[i] = yac_huiliers_area_info(overlapCells[i], overlapBarycenters[i]);
+                  normalise_vector(overlapBarycenters[i]);
+                }
+            }
+          else { overlapAreas[i] = 0.0; }
+        }
+      return;
+    }
+
+  double coordinates_xyz[3][3] = { { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 } };
+  enum yac_edge_type edgeTypes[3] = { GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE };
+
+  struct grid_cell targetPartialCell;
+  targetPartialCell.array_size = 3;
+  targetPartialCell.num_corners = 3;
+  targetPartialCell.coordinates_xyz = coordinates_xyz;
+  targetPartialCell.edge_type = edgeTypes;
+
+  // Do the clipping and get the cell for the overlapping area
+
+  for (size_t i = 0; i < numSearchCells; i++) partialAreas[i] = 0.0;
+
+  // common node point to all partial target cells
+  auto partial_cell_xyz = targetPartialCell.coordinates_xyz;
+  auto cell_xyz = targetCell.coordinates_xyz;
+
+  LL_to_XYZ(tgtLon, tgtLat, partial_cell_xyz[0]);
+
+  for (size_t cornerNum = 0; cornerNum < targetCell.num_corners; ++cornerNum)
+    {
+      auto cornerA = cornerNum;
+      auto cornerB = (cornerNum + 1) % targetCell.num_corners;
+
+      // skip clipping and area calculation for degenerated triangles
+      //
+      // If this is not sufficient, instead we can try something like:
+      //
+      //     point_list target_list
+      //     init_point_list(&target_list);
+      //     generate_point_list(&target_list, targetCell);
+      //     grid_cell temp_target_cell;
+      //     generate_overlap_cell(target_list, temp_target_cell);
+      //     free_point_list(&target_list);
+      //
+      // and use temp_target_cell for triangulation.
+      //
+      // Compared to the if statement below the alternative seems to be quite costly.
+
+      if (points_are_identically(cell_xyz[cornerA], cell_xyz[cornerB])) continue;
+
+      for (int k = 0; k < 3; ++k) partial_cell_xyz[1][k] = cell_xyz[cornerA][k];
+      for (int k = 0; k < 3; ++k) partial_cell_xyz[2][k] = cell_xyz[cornerB][k];
+
+      yac_cell_clipping(numSearchCells, sourceCells.data(), targetPartialCell, overlapCells.data());
+
+      // Get the partial areas for the overlapping regions as sum over the partial target cells.
+      for (size_t i = 0; i < numSearchCells; i++)
+        {
+          if (overlapCells[i].num_corners == 0) continue;
+
+          if (overlapBarycenters == NULL)
+            overlapAreas[i] += gridcell_area(overlapCells[i]);
+          else
+            overlapAreas[i] += yac_huiliers_area_info(overlapCells[i], overlapBarycenters[i]);
+        }
+    }
+
+  if (overlapBarycenters != NULL)
+    for (size_t i = 0; i < numSearchCells; i++)
+      if (overlapAreas[i] > 0.0) normalise_vector(overlapBarycenters[i]);
+
+#ifdef VERBOSE
+  for (size_t i = 0; i < numSearchCells; i++) printf("overlap area %zu: %lf\n", i, overlapAreas[i]);
+#endif
+}
+
+static void
+cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, const GridCell &gridCell, double tgtLon, double tgtLat)
+{
+  auto partialAreas = search.partialAreas.data();
+  cdo_compute_concave_overlap_info(numSearchCells, search, gridCell, tgtLon, tgtLat, partialAreas, NULL);
+}
+*/
+
+static double
+get_edge_direction(const double *ref_corner, double *corner_a, double *corner_b)
+{
+  double edge_norm[3];
+  crossproduct_kahan(corner_a, corner_b, edge_norm);
+  normalise_vector(edge_norm);
+
+  // sine of the angle between the edge and the reference corner
+  double angle = edge_norm[0] * ref_corner[0] + edge_norm[1] * ref_corner[1] + edge_norm[2] * ref_corner[2];
+
+  // if the reference corner is directly on the edge
+  // (for small angles sin(x)==x)
+  if (fabs(angle) < yac_angle_tol) return 0.0;
+
+  return copysign(1.0, angle);
+}
+
+static void
+cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, const GridCell &gridCell)
+{
+  auto targetCell = gridCell.yacGridCell;
+  auto &overlapAreas = search.partialAreas;
+  auto &overlapCells = search.overlapCells;
+  auto &sourceCells = search.gridCells;
+
+  // common node point to all partial target cells
+  double *baseCorner = targetCell.coordinates_xyz[0];
+
+  // the triangulation algorithm only works for cells that only have great circle edges
+  enum yac_edge_type edgeTypes[3] = { GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE };
+  double coordinates_xyz[3][3] = { { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 } };
+  for (int k = 0; k < 3; ++k) coordinates_xyz[0][k] = baseCorner[k];
+
+  // data structure to hold the triangles of the target cell
+  struct grid_cell partialCell;
+  partialCell.array_size = 3;
+  partialCell.num_corners = 3;
+  partialCell.coordinates_xyz = coordinates_xyz;
+  partialCell.edge_type = edgeTypes;
+
+  // Do the clipping and get the cell for the overlapping area
+
+  for (size_t i = 0; i < numSearchCells; ++i) overlapAreas[i] = 0.0;
+
+  // for all triangles of the target cell
+  // (triangles a formed by first corner of the target cells and each edge of
+  //  the cell; the first and last edge of the cell already, contain the
+  //  first corner, therefore we can skip them)
+  for (size_t cornerIdx = 1; cornerIdx < targetCell.num_corners - 1; ++cornerIdx)
+    {
+      auto cornerA = targetCell.coordinates_xyz[cornerIdx];
+      auto cornerB = targetCell.coordinates_xyz[(cornerIdx + 1)];
+
+      // if the current edge has a length of zero
+      if (points_are_identically(cornerA, cornerB)) continue;
+
+      auto edgeDirection = get_edge_direction(baseCorner, cornerA, cornerB);
+
+      for (int k = 0; k < 3; ++k) partialCell.coordinates_xyz[1][k] = cornerA[k];
+      for (int k = 0; k < 3; ++k) partialCell.coordinates_xyz[2][k] = cornerB[k];
+
+      // clip the current target cell triangle with all source cells
+      yac_cell_clipping(numSearchCells, sourceCells.data(), partialCell, overlapCells.data());
+
+      // Get the partial areas for the overlapping regions as sum over the partial target cells.
+      for (size_t i = 0; i < numSearchCells; ++i)
+        {
+          if (overlapCells[i].num_corners == 0) continue;
+
+          overlapAreas[i] += gridcell_area(overlapCells[i]) * edgeDirection;
+        }
+    }
+
+  for (size_t i = 0; i < numSearchCells; ++i)
+    {
+      if (overlapAreas[i] < 0.0) overlapAreas[i] = -overlapAreas[i];
+    }
+
+#ifdef VERBOSE
+  for (size_t i = 0; i < numSearchCells; i++) printf("overlap area %zu: %lf\n", i, overlapAreas[i]);
+#endif
+}
+
+static void
+set_cell_coordinates_yac(const size_t cellIndex, const size_t numCorners, const GridInfo &gridInfo,
+                         const struct grid_cell &yacGridCell, double *x, double *y)
+{
+  auto storeXY = (x && y);
+  auto xyz = yacGridCell.coordinates_xyz;
+
+  const auto cellCornersLon = &gridInfo.cellCornersLon[cellIndex * numCorners];
+  const auto cellCornersLat = &gridInfo.cellCornersLat[cellIndex * numCorners];
+  for (size_t i = 0; i < numCorners; ++i) LL_to_XYZ(cellCornersLon[i], cellCornersLat[i], xyz[i]);
+
+  if (storeXY)
+    {
+      for (size_t k = 0; k < numCorners; ++k)
+        {
+          x[k] = cellCornersLon[k];
+          y[k] = cellCornersLat[k];
+        }
+    }
+}
+
+static void
+set_cell_coordinates(const size_t cellIndex, const size_t numCorners, const GridInfo &gridInfo, const GridCell &gridCell)
+{
+  set_cell_coordinates_yac(cellIndex, numCorners, gridInfo, gridCell.yacGridCell, gridCell.coordinates_x, gridCell.coordinates_y);
+}
+
+static void
+set_coordinates_yac(const size_t numCells, const Varray<size_t> &cellIndices, const size_t numCorners, const GridInfo &gridInfo,
+                    const Varray<grid_cell> &yacGridCell)
+{
+  for (size_t i = 0; i < numCells; ++i)
+    set_cell_coordinates_yac(cellIndices[i], numCorners, gridInfo, yacGridCell[i], nullptr, nullptr);
+}
+
+static void
+set_coordinates_yac(const double (*xyzCoords)[3], const size_t numCells, const Varray<size_t> &cellIndices, const size_t numCorners,
+                    const Varray<grid_cell> &yacGridCell)
+{
+  for (size_t i = 0; i < numCells; ++i)
+    {
+      auto offset = cellIndices[i] * numCorners;
+      auto xyz = yacGridCell[i].coordinates_xyz;
+      for (size_t k = 0; k < numCorners; ++k)
+        for (size_t l = 0; l < 3; ++l) xyz[k][l] = xyzCoords[offset + k][l];
+    }
+}
+
+static void
+gridcell_init_yac(GridCell &gridCell, size_t numCorners, enum yac_edge_type *edgeType)
+{
+  gridCell.yacGridCell.array_size = numCorners;
+  gridCell.yacGridCell.num_corners = numCorners;
+  gridCell.yacGridCell.edge_type = edgeType;
+  gridCell.yacGridCell.coordinates_xyz = new double[numCorners][3];
+  gridCell.coordinates_x = new double[numCorners];
+  gridCell.coordinates_y = new double[numCorners];
+}
+
+static void
+gridcell_free_yac(const GridCell &gridCell)
+{
+  delete[] gridCell.yacGridCell.coordinates_xyz;
+  delete[] gridCell.coordinates_x;
+  delete[] gridCell.coordinates_y;
+}
+
+static size_t
+remove_invalid_areas(size_t numSearchCells, Varray<double> &areas, Varray<size_t> &searchIndices)
+{
+  size_t n = 0;
+  for (size_t i = 0; i < numSearchCells; ++i)
+    {
+      if (areas[i] > 0.0)
+        {
+          areas[n] = areas[i];
+          searchIndices[n] = searchIndices[i];
+          n++;
+        }
+    }
+
+  return n;
+}
+
+static size_t
+remove_invalid_weights(size_t gridSize, size_t numWeights, Varray<double> &weights, Varray<size_t> &searchIndices)
+{
+  size_t n = 0;
+  for (size_t i = 0; i < numWeights; ++i)
+    {
+      auto cellIndex = (weights[i] > 0.0) ? searchIndices[i] : gridSize;
+      if (cellIndex != gridSize)
+        {
+          weights[n] = weights[i];
+          searchIndices[n] = cellIndex;
+          n++;
+        }
+    }
+
+  return n;
+}
+
+static size_t
+remove_unmask_weights(const Varray<short> &gridMask, size_t numWeights, Varray<double> &weights, Varray<size_t> &searchIndices)
+{
+  size_t n = 0;
+  for (size_t i = 0; i < numWeights; ++i)
+    {
+      auto cellIndex = searchIndices[i];
+      /*
+        Store the appropriate addresses and weights.
+        Also add contributions to cell areas.
+        The source grid mask is the master mask.
+      */
+      if (gridMask[cellIndex])
+        {
+          weights[n] = weights[i];
+          searchIndices[n] = cellIndex;
+          n++;
+        }
+    }
+
+  return n;
+}
+
+static void
+normalize_weights(NormOpt normOpt, double cellArea, double cellFrac, size_t numWeights, Varray<double> &weights)
+{
+  if (normOpt == NormOpt::DESTAREA)
+    {
+      auto normFactor = is_not_equal(cellArea, 0.0) ? 1.0 / cellArea : 0.0;
+      for (size_t i = 0; i < numWeights; ++i) weights[i] *= normFactor;
+    }
+  else if (normOpt == NormOpt::FRACAREA)
+    {
+      auto normFactor = is_not_equal(cellFrac, 0.0) ? 1.0 / cellFrac : 0.0;
+      for (size_t i = 0; i < numWeights; ++i) weights[i] *= normFactor;
+    }
+  else if (normOpt == NormOpt::NONE) {}
+}
+
+static void
+correct_weights(double cellArea, size_t numWeights, Varray<double> &weights)
+{
+  for (size_t i = 0; i < numWeights; ++i) weights[i] /= cellArea;
+  yac_correct_weights(numWeights, weights.data());
+  for (size_t i = 0; i < numWeights; ++i) weights[i] *= cellArea;
+}
+
+static void
+sort_weights(size_t numWeights, Varray<size_t> &searchIndices, Varray<double> &weights)
+{
+  size_t n;
+  for (n = 1; n < numWeights; ++n)
+    if (searchIndices[n] < searchIndices[n - 1]) break;
+  if (n == numWeights) return;
+
+  if (numWeights > 1)
+    {
+      struct IndexWeightX
+      {
+        size_t index;
+        double weight;
+      };
+
+      Varray<IndexWeightX> indexWeights(numWeights);
+
+      for (n = 0; n < numWeights; ++n)
+        {
+          indexWeights[n].index = searchIndices[n];
+          indexWeights[n].weight = weights[n];
+        }
+
+      const auto compareIndex = [](const auto &a, const auto &b) noexcept { return a.index < b.index; };
+      std::sort(indexWeights.begin(), indexWeights.end(), compareIndex);
+
+      for (n = 0; n < numWeights; ++n)
+        {
+          searchIndices[n] = indexWeights[n].index;
+          weights[n] = indexWeights[n].weight;
+        }
+    }
+}
+
+static int
+get_lonlat_circle_index(size_t numCells, size_t numCorners, const Varray<double> &clon, const Varray<double> &clat)
+{
+  int lonlatCircleIndex = -1;
+
+  if (numCorners == 4)
+    {
+      size_t iadd = (numCells < 100) ? 1 : numCells / 30 - 1;
+      size_t num_i = 0, num_eq0 = 0, num_eq1 = 0;
+
+      for (size_t i = 0; i < numCells; i += iadd)
+        {
+          auto i4 = i * 4;
+          num_i++;
+          // clang-format off
+          if (is_equal(clon[i4 + 1], clon[i4 + 2]) && is_equal(clon[i4 + 3], clon[i4 + 0]) &&
+              is_equal(clat[i4 + 0], clat[i4 + 1]) && is_equal(clat[i4 + 2], clat[i4 + 3]))
+            {
+              num_eq1++;
+            }
+          else if (is_equal(clon[i4 + 0], clon[i4 + 1]) && is_equal(clon[i4 + 2], clon[i4 + 3]) &&
+                   is_equal(clat[i4 + 1], clat[i4 + 2]) && is_equal(clat[i4 + 3], clat[i4 + 0]))
+            {
+              num_eq0++;
+            }
+          // clang-format on
+        }
+
+      if (num_i == num_eq1) lonlatCircleIndex = 1;
+      if (num_i == num_eq0) lonlatCircleIndex = 0;
+    }
+
+  // printf("lonlatCircleIndex %d\n", lonlatCircleIndex);
+
+  return lonlatCircleIndex;
+}
+
+static int
+get_lonlat_circle_index(const GridInfo &gridInfo)
+{
+  return get_lonlat_circle_index(gridInfo.numCells, gridInfo.numCorners, gridInfo.cellCornersLon, gridInfo.cellCornersLat);
+}
+
+static double
+conserv_remap(const Varray<double> &srcArray, size_t numWeights, const Varray<double> &weights, const Varray<size_t> &srcIndices)
+{
+  double tgtPoint = 0.0;
+  for (size_t i = 0; i < numWeights; ++i) tgtPoint += srcArray[srcIndices[i]] * weights[i];
+
+  return tgtPoint;
+}
+
+void
+remap_conserv2(NormOpt normOpt, const RemapSearch &remapSearch, const Varray<double> &srcArray, Varray<double> &tgtArray,
+               double missval)
+{
+  auto &srcGrid = remapSearch.srcGrid;
+  auto &tgtGrid = remapSearch.tgtGrid;
+
+  auto srcNumCells = srcGrid.numCells;
+  auto tgtNumCells = tgtGrid.numCells;
+
+  Varray<short> srcGridMask(srcNumCells);
+  for (size_t i = 0; i < srcNumCells; ++i) srcGridMask[i] = !is_equal(srcArray[i], missval);
+
+  auto srcNumCorners = srcGrid.numCorners;
+  auto tgtNumCorners = tgtGrid.numCorners;
+
+  enum yac_edge_type lonlatCircleType[] = { LON_CIRCLE_EDGE, LAT_CIRCLE_EDGE, LON_CIRCLE_EDGE, LAT_CIRCLE_EDGE, LON_CIRCLE_EDGE };
+  Varray<enum yac_edge_type> greatCircleType(std::max(srcNumCorners, tgtNumCorners), GREAT_CIRCLE_EDGE);
+
+  auto srcEdgeType = greatCircleType.data();
+  auto tgtEdgeType = greatCircleType.data();
+
+  enum yac_cell_type tgtCellType = MIXED_CELL;
+
+  if (srcNumCorners == 4)
+    {
+      auto lonlatCircleIndex = get_lonlat_circle_index(srcGrid);
+      if (lonlatCircleIndex >= 0) srcEdgeType = &lonlatCircleType[lonlatCircleIndex];
+    }
+
+  if (tgtNumCorners == 4)
+    {
+      auto lonlatCircleIndex = get_lonlat_circle_index(tgtGrid);
+      if (lonlatCircleIndex >= 0)
+        {
+          tgtCellType = LON_LAT_CELL;
+          tgtEdgeType = &lonlatCircleType[lonlatCircleIndex];
+        }
+    }
+
+  GridCell tgtGridCell;
+  gridcell_init_yac(tgtGridCell, tgtNumCorners, tgtEdgeType);
+
+  CellSearch cellSearch;
+  cellSearch.numCellCorners = srcNumCorners;
+  cellSearch.edgeType = srcEdgeType;
+
+  auto numCellCorners = srcNumCorners;  // num of corners of search cells
+
+  Varray<size_t> searchIndices(srcNumCells);
+
+  // Loop over target grid
+  for (size_t tgtCellIndex = 0; tgtCellIndex < tgtNumCells; ++tgtCellIndex)
+    {
+      tgtArray[tgtCellIndex] = missval;
+
+      // if (!tgtGrid.mask[tgtCellIndex]) continue;
+
+      set_cell_coordinates(tgtCellIndex, tgtNumCorners, tgtGrid, tgtGridCell);
+
+      // Get search cells
+      auto numSearchCells = do_gridcell_search(remapSearch.gcs, false, tgtGridCell, searchIndices);
+
+      if (numSearchCells == 0) continue;
+
+      // Create search arrays
+
+      cellsearch_realloc(numSearchCells, cellSearch);
+
+      // set_coordinates_yac(numSearchCells, searchIndices, numCellCorners, srcGrid, cellSearch.gridCells);
+      if (remapSearch.gcs.xyzCoords)
+        set_coordinates_yac(remapSearch.gcs.xyzCoords, numSearchCells, searchIndices, numCellCorners, cellSearch.gridCells);
+      else
+        set_coordinates_yac(numSearchCells, searchIndices, numCellCorners, srcGrid, cellSearch.gridCells);
+
+      if (tgtNumCorners < 4 || tgtCellType == LON_LAT_CELL)
+        cdo_compute_overlap_areas(numSearchCells, cellSearch, tgtGridCell);
+      else
+        cdo_compute_concave_overlap_areas(numSearchCells, cellSearch, tgtGridCell);
+
+      // For all supermesh cells compute the area and normalised area.
+      // Additionally, remove all empty supermesh cells.
+      size_t new_num_super_cells = 0;
+      size_t tgt_idx = 0;
+      /*
+      for (size_t i = 0, j = 0; i < total_num_overlaps;)
+        {
+
+          // get information about the current target cell
+          size_t curr_tgt_cell = super_cells[i].tgt.local_id;
+          yac_const_basic_grid_data_get_grid_cell(tgt_basic_grid_data, curr_tgt_cell, &tgt_grid_cell);
+
+          double curr_tgt_cell_coverage = 0.0;
+
+          // for all supermesh cells overlapping with the current target cell
+          for (;(i < total_num_overlaps) && (super_cells[i].tgt.local_id == curr_tgt_cell); ++i)
+            {
+
+              // get the current source cell
+              yac_const_basic_grid_data_get_grid_cell(src_basic_grid_data, super_cells[i].src.local_id, &src_grid_cell);
+
+              // compute area of the current supermesh cell
+              double super_cell_area;
+              double barycenter[3];
+              cdo_compute_concave_overlap_info(1, cellSearch, tgtGridCell,
+                                               tgtGrid.cellCentersLon[tgtCellIndex], tgtGrid.cellCentersLat[tgtCellIndex],
+                                               &super_cell_area, &barycenter);
+            }
+
+          // if there is an overlap between the current source and target cell
+          if (super_cell_area > 0.0)
+            {
+              super_cells[new_num_super_cells].src = super_cells[i].src;
+              super_cells[new_num_super_cells].tgt = super_cells[i].tgt;
+              super_cells[new_num_super_cells].area = super_cell_area;
+              memcpy(super_cells[new_num_super_cells].barycenter, barycenter, 3 * sizeof(double));
+              super_cells[new_num_super_cells].src_cell_gradient = NULL;
+              ++new_num_super_cells;
+
+              curr_tgt_cell_coverage += super_cell_area;
+            }
+        }
+      */
+      auto &partialWeights = cellSearch.partialAreas;
+
+      auto numWeights = remove_invalid_areas(numSearchCells, partialWeights, searchIndices);
+
+      auto tgtCellArea = gridcell_area(tgtGridCell.yacGridCell);
+
+      if (normOpt == NormOpt::FRACAREA) correct_weights(tgtCellArea, numWeights, partialWeights);
+
+      numWeights = remove_invalid_weights(srcNumCells, numWeights, partialWeights, searchIndices);
+      numWeights = remove_unmask_weights(srcGridMask, numWeights, partialWeights, searchIndices);
+
+      auto tgtCellFrac = std::accumulate(partialWeights.begin(), partialWeights.begin() + numWeights, 0.0);
+
+      if (numWeights)
+        {
+          sort_weights(numWeights, searchIndices, partialWeights);
+          // Normalize weights using target cell area if requested
+          normalize_weights(normOpt, tgtCellArea, tgtCellFrac, numWeights, partialWeights);
+          tgtArray[tgtCellIndex] = conserv_remap(srcArray, numWeights, partialWeights, searchIndices);
+        }
+    }
+
+  // Finished with all cells: deallocate search arrays
+
+  cellsearch_free(cellSearch);
+  gridcell_free_yac(tgtGridCell);
+}  // remap_conserv2
diff --git a/src/lib/yac/cdo_remap_conserv_test.cc b/src/lib/yac/cdo_remap_conserv_test.cc
index a99fdf169ef03c53e6d4a2ba587386462ac2b20f..883dbad660523cb41bfd8208cc812ee910a02e06 100644
--- a/src/lib/yac/cdo_remap_conserv_test.cc
+++ b/src/lib/yac/cdo_remap_conserv_test.cc
@@ -1,6 +1,6 @@
 /*
     gcc -DYAC_FOR_CDO -Wall -g -O3 -c *.c
-    g++ -DYAC_FOR_CDO -Drestrict= -Wall -g -O3 cdo_remap_conserv_test.cc cdo_grid.cc cdo_cell_search.cc cdo_remap_conserv.cc *.o
+    g++ -DYAC_FOR_CDO -Drestrict= -Wall -g -O3 cdo_remap_conserv_test.cc cdo_grid.cc cdo_cell_search.cc cdo_remap_conserv.cc cdo_remap_conserv2.cc *.o
  */
 
 #include "cdo_remap.h"
@@ -13,7 +13,7 @@ cdo_generate_grid_reg2d(GridInfo &grid, double *coordinates_x, double *coordinat
 
 static void test1()
 {
-  const double missval = -1.0;
+  double missval = -1.0;
   RemapSearch remapSearch;
 
   auto &srcGrid = remapSearch.srcGrid;
@@ -74,7 +74,7 @@ static void test1()
 
 static void test3()
 {
-  const double missval = -1.0;
+  double missval = -1.0;
   RemapSearch remapSearch;
 
   auto &srcGrid = remapSearch.srcGrid;
@@ -152,7 +152,7 @@ static void test3()
 
 static void test4()
 {
-  const double missval = -1.0;
+  double missval = -1.0;
   RemapSearch remapSearch;
 
   auto &srcGrid = remapSearch.srcGrid;
@@ -258,7 +258,7 @@ static void test4()
 
 static void test6()
 {
-  const double missval = -1.0;
+  double missval = -1.0;
   RemapSearch remapSearch;
 
   auto &srcGrid = remapSearch.srcGrid;
@@ -331,7 +331,7 @@ static void test6()
 
       gridcell_search_create(remapSearch.gcs, srcGrid.numCells, srcGrid.numCorners, srcGrid.cellCornersLon, srcGrid.cellCornersLat);
 
-      //remap_conserv2(NormOpt::DESTAREA, remapSearch, srcField, tgtField, missval);
+      remap_conserv2(NormOpt::DESTAREA, remapSearch, srcField, tgtField, missval);
 
       gridcell_search_delete(remapSearch.gcs);
 
@@ -364,7 +364,7 @@ int main(void)
   test4();
 
   // 2nd order conservativ remapping
-  // test6();
+  //test6();
 
   return 0;
 }
diff --git a/src/lib/yac/cdo_remap_conserv_test2.cc b/src/lib/yac/cdo_remap_conserv_test2.cc
deleted file mode 100644
index 037d97c12863b2a0b363db44c78b6eebdb4659fa..0000000000000000000000000000000000000000
--- a/src/lib/yac/cdo_remap_conserv_test2.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-    gcc -DYAC_FOR_CDO -Wall -g -O3 -c *.c
-    g++ -DYAC_FOR_CDO -Drestrict= -Wall -g -O3 *.cc *.o
- */
-
-#include "cdo_remap.h"
-
-static void
-compute_testfield(Varray<double> &array, const Varray<double> &cellCentersLon, const Varray<double> &cellCentersLat)
-{
-  double xyz[3];
-  const auto numCells = array.size();
-  for (size_t i = 0; i < numCells; ++i)
-    {
-      LL_to_XYZ(cellCentersLon[i], cellCentersLat[i], xyz);
-      const auto x = xyz[0];
-      const auto y = xyz[1];
-      const auto z = xyz[2];
-      array[i] = 1.0 + std::pow(x, 8.0) + std::exp(2.0 * y * y * y) + std::exp(2.0 * x * x) + 10.0 * x * y * z;
-    }
-}
-
-static
-void output_ext(const Varray<double> &array)
-{
-  const auto numCells = array.size();
-  fprintf(stdout, "%8d %4d %8g %8zu\n", 010101, 1, 0.0, numCells);
-
-  int nout = 0;
-  for (size_t i = 0; i < numCells; i++)
-    {
-      if (nout == 6)
-        {
-          nout = 0;
-          fprintf(stdout, "\n");
-        }
-      fprintf(stdout, " %12.6g", array[i]);
-      nout++;
-    }
-  fprintf(stdout, "\n");
-}
-
-static
-void grid_init_regular(GridInfo &grid, double increment)
-{
-  generate_grid_lonlat(grid, increment, -180, 180, -90, 90);
-}
-
-int main(void)
-{
-  const double missval = -9.e33;
-  RemapSearch remapSearch;
-
-  auto &srcGrid = remapSearch.srcGrid;
-  auto &tgtGrid = remapSearch.tgtGrid;
-
-  grid_init_regular(srcGrid, 0.5);
-  grid_init_regular(tgtGrid, 2.5);
-
-  Varray<double> srcArray(srcGrid.numCells);
-  Varray<double> tgtArray(tgtGrid.numCells);
-
-  compute_testfield(srcArray, srcGrid.cellCentersLon, srcGrid.cellCentersLat);
-
-  gridcell_search_create(remapSearch.gcs, srcGrid.numCells, srcGrid.numCorners, srcGrid.cellCornersLon, srcGrid.cellCornersLat);
-
-  remap_conserv(NormOpt::FRACAREA, remapSearch, srcArray, tgtArray, missval);
-
-  // output_ext(srcArray);
-  // output_ext(tgtArray);
-
-  gridcell_search_delete(remapSearch.gcs);
-
-  return 0;
-}
diff --git a/src/lib/yac/check_overlap.c b/src/lib/yac/check_overlap.c
index f8cab00a6dd19b31196abd165027ee0616afb4d3..863c030547f4ff48ab00f20d7175c82907912745 100644
--- a/src/lib/yac/check_overlap.c
+++ b/src/lib/yac/check_overlap.c
@@ -1,62 +1,10 @@
-/**
- * @file check_overlap.c
- * @brief Set of functions to determine an overlap between any two cells 
- *
- * Compared to \ref check_overlap.c these routine take care of how vertex
- * points of a cell are connected, either by great circles or along the loxodrome.
- *
- * very interesting literature:
- * - http://geospatialmethods.org/spheres/GCIntersect.html
- *
- * Note: Not all functions are documented by Doxygen. See the source code
- * and \ref geometry.h for further details.
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdlib.h>
 #include <math.h>
-#include "utils.h"
+#include "utils_core.h"
 #include "geometry.h"
 #include "grid_cell.h"
 
@@ -170,9 +118,9 @@ static int point_in_cell_unstruct_triangle(
 
   double cross_edges[3][3];
 
-  crossproduct_ld(a, b, &(cross_edges[0][0]));
-  crossproduct_ld(b, c, &(cross_edges[1][0]));
-  crossproduct_ld(c, a, &(cross_edges[2][0]));
+  crossproduct_kahan(a, b, &(cross_edges[0][0]));
+  crossproduct_kahan(b, c, &(cross_edges[1][0]));
+  crossproduct_kahan(c, a, &(cross_edges[2][0]));
 
   double sq_sin_edge[3] = {
     cross_edges[0][0] * cross_edges[0][0] +
@@ -274,9 +222,9 @@ static int point_in_cell_unstruct_triangle(
 
   // double cross_edges[3][3];
 
-  // crossproduct_ld(a, b, &(cross_edges[0][0]));
-  // crossproduct_ld(b, c, &(cross_edges[1][0]));
-  // crossproduct_ld(c, a, &(cross_edges[2][0]));
+  // crossproduct_kahan(a, b, &(cross_edges[0][0]));
+  // crossproduct_kahan(b, c, &(cross_edges[1][0]));
+  // crossproduct_kahan(c, a, &(cross_edges[2][0]));
 
   // double sq_sin_edge[3] = {
     // cross_edges[0][0] * cross_edges[0][0] +
@@ -396,10 +344,10 @@ static int point_in_cell_unstruct_quad(
 
   double cross[4][3];
 
-  crossproduct_ld(a, b, &(cross[0][0]));
-  crossproduct_ld(b, c, &(cross[1][0]));
-  crossproduct_ld(c, d, &(cross[2][0]));
-  crossproduct_ld(d, a, &(cross[3][0]));
+  crossproduct_kahan(a, b, &(cross[0][0]));
+  crossproduct_kahan(b, c, &(cross[1][0]));
+  crossproduct_kahan(c, d, &(cross[2][0]));
+  crossproduct_kahan(d, a, &(cross[3][0]));
 
   double sq_sin_edge[4] = {
     cross[0][0]*cross[0][0]+cross[0][1]*cross[0][1]+cross[0][2]*cross[0][2],
@@ -456,10 +404,10 @@ static int point_in_cell_unstruct_quad(
 
   // double cross[4][3];
 
-  // crossproduct_ld(a, b, &(cross[0][0]));
-  // crossproduct_ld(b, c, &(cross[1][0]));
-  // crossproduct_ld(c, d, &(cross[2][0]));
-  // crossproduct_ld(d, a, &(cross[3][0]));
+  // crossproduct_kahan(a, b, &(cross[0][0]));
+  // crossproduct_kahan(b, c, &(cross[1][0]));
+  // crossproduct_kahan(c, d, &(cross[2][0]));
+  // crossproduct_kahan(d, a, &(cross[3][0]));
 
   // double sq_sin_edge[4] = {
     // cross[0][0]*cross[0][0]+cross[0][1]*cross[0][1]+cross[0][2]*cross[0][2],
diff --git a/src/lib/yac/clipping.c b/src/lib/yac/clipping.c
index 9c12446ee1cf984a026ba9c05a781615a09cb9e0..f941ba3eb24f1cbf660643b3636aa7a12a053037 100644
--- a/src/lib/yac/clipping.c
+++ b/src/lib/yac/clipping.c
@@ -1,48 +1,6 @@
-/**
- * @file clipping.c
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifdef HAVE_CONFIG_H
 // Get the definition of the 'restrict' keyword.
@@ -54,12 +12,11 @@
 #include <string.h>
 #include <math.h>
 
-#include "yac_ld_interface.h"
 #include "geometry.h"
 #include "clipping.h"
 #include "area.h"
 #include "ensure_array_size.h"
-#include "utils.h"
+#include "utils_core.h"
 
 //#define YAC_VERBOSE_CLIPPING
 
@@ -133,7 +90,7 @@ static double get_edge_direction(
   double * ref_corner, double * corner_a, double * corner_b) {
 
   double edge_norm[3];
-  crossproduct_ld(corner_a, corner_b, edge_norm);
+  crossproduct_kahan(corner_a, corner_b, edge_norm);
   normalise_vector(edge_norm);
 
   // sine of the angle between the edge and the reference corner
@@ -317,7 +274,7 @@ void yac_compute_overlap_areas (size_t N,
 static enum yac_cell_type get_cell_type(struct grid_cell target_cell) {
 
   YAC_ASSERT(
-    target_cell.num_corners > 0, "ERROR(get_cell_type): CELL without edge")
+    target_cell.num_corners > 0, "ERROR(get_cell_type): cell without edge")
 
   enum yac_cell_type cell_type = MIXED_CELL;
 
@@ -396,34 +353,20 @@ int yac_circle_compare(void const * a, void const * b) {
       default:
       case (GREAT_CIRCLE):
         for (int i = 0; !ret && (i < 3); ++i) {
-#ifndef USE_MPF
           ret =
             (circle_a->data.gc.norm_vector[i] >
              circle_b->data.gc.norm_vector[i]) -
             (circle_a->data.gc.norm_vector[i] <
              circle_b->data.gc.norm_vector[i]);
-#else
-          ret = (mpf_cmp(circle_a->data.gc.norm_vector[i],
-                         circle_b->data.gc.norm_vector[i]) > 0) -
-                (mpf_cmp(circle_a->data.gc.norm_vector[i],
-                         circle_b->data.gc.norm_vector[i]) < 0);
-#endif
         }
         break;
       case (LON_CIRCLE):
         for (int i = 0; !ret && (i < 3); ++i) {
-#ifndef USE_MPF
           ret =
             (circle_a->data.lon.norm_vector[i] >
              circle_b->data.lon.norm_vector[i]) -
             (circle_a->data.lon.norm_vector[i] <
              circle_b->data.lon.norm_vector[i]);
-#else
-          ret = (mpf_cmp(circle_a->data.lon.norm_vector[i],
-                         circle_b->data.lon.norm_vector[i]) > 0) -
-                (mpf_cmp(circle_a->data.lon.norm_vector[i],
-                         circle_b->data.lon.norm_vector[i]) < 0);
-#endif
         }
         break;
       case (LAT_CIRCLE):
@@ -443,86 +386,19 @@ int yac_circle_compare(void const * a, void const * b) {
   return ret;
 }
 
-#ifndef USE_MPF
-static void compute_norm_vector_ld(
-  double const a[3], double const b[3], long double norm_vector[3]) {
 
-  long double const a_[3] = {a[0], a[1], a[2]};
-  long double const b_[3] = {b[0], b[1], b[2]};
+static void compute_norm_vector_kahan(
+  double const a[3], double const b[3], double norm_vector[3]) {
 
-  norm_vector[0] = a_[1] * b_[2] - a_[2] * b_[1];
-  norm_vector[1] = a_[2] * b_[0] - a_[0] * b_[2];
-  norm_vector[2] = a_[0] * b_[1] - a_[1] * b_[0];
+  crossproduct_kahan(a, b, norm_vector);
 
-  long double scale = 1.0 / sqrtl(norm_vector[0] * norm_vector[0] +
-                                  norm_vector[1] * norm_vector[1] +
-                                  norm_vector[2] * norm_vector[2]);
+  double scale = 1.0 / sqrt(norm_vector[0] * norm_vector[0] +
+                            norm_vector[1] * norm_vector[1] +
+                            norm_vector[2] * norm_vector[2]);
   norm_vector[0] *= scale;
   norm_vector[1] *= scale;
   norm_vector[2] *= scale;
 }
-#else
-static void compute_norm_vector_ld(
-  double const a[3], double const b[3], mpf_t norm_vector[3]) {
-
-   mpf_t a_[3], b_[3];
-   yac_mpf_set_vector_d(a_, a);
-   yac_mpf_set_vector_d(b_, b);
-
-   // norm_vector = a_[1] * b_[2]
-   mpf_mul(norm_vector[0], a_[1], b_[2]);
-   // norm_vector = a_[2] * b_[0]
-   mpf_mul(norm_vector[1], a_[2], b_[0]);
-   // norm_vector = a_[0] * b_[1]
-   mpf_mul(norm_vector[2], a_[0], b_[1]);
-
-   mpf_t buf;
-   mpf_init2(buf, YAC_MPF_PRECISION);
-
-   // buf = a_[2] * b_[1]
-   mpf_mul(buf, a_[2], b_[1]);
-   // norm_vector[0] -= buf
-   mpf_sub(norm_vector[0], norm_vector[0], buf);
-   // buf = a_[0] * b_[2]
-   mpf_mul(buf, a_[0], b_[2]);
-   // norm_vector[1] -= buf
-   mpf_sub(norm_vector[1], norm_vector[1], buf);
-   // buf = a_[1] * b_[0]
-   mpf_mul(buf, a_[1], b_[0]);
-   // norm_vector[2] -= buf
-   mpf_sub(norm_vector[2], norm_vector[2], buf);
-
-   mpf_t scale;
-   mpf_init2(scale, YAC_MPF_PRECISION);
-
-   // scale = norm_vector[0] * norm_vector[0]
-   mpf_mul(scale, norm_vector[0], norm_vector[0]);
-   // buf = norm_vector[1] * norm_vector[1]
-   mpf_mul(buf, norm_vector[1], norm_vector[1]);
-   // scale += buf
-   mpf_add(scale, scale, buf);
-   // buf = norm_vector[2] * norm_vector[2]
-   mpf_mul(buf, norm_vector[2], norm_vector[2]);
-   // scale += buf
-   mpf_add(scale, scale, buf);
-   // scale = sqrt(scale)
-   mpf_sqrt(scale, scale);
-   // scale = 1.0 / scale
-   mpf_ui_div(scale, 1, scale);
-
-   // norm_vector[0] *= scale
-   mpf_mul(norm_vector[0], norm_vector[0], scale);
-   // norm_vector[1] *= scale
-   mpf_mul(norm_vector[1], norm_vector[1], scale);
-   // norm_vector[2] *= scale
-   mpf_mul(norm_vector[2], norm_vector[2], scale);
-
-   yac_mpf_clear_vector(a_);
-   yac_mpf_clear_vector(b_);
-   mpf_clear(buf);
-   mpf_clear(scale);
-}
-#endif
 
 void yac_circle_generate(
   double const * a, double const * b, enum yac_edge_type type,
@@ -553,17 +429,11 @@ void yac_circle_generate(
       default:
       case(GREAT_CIRCLE_EDGE):
         circle->type = GREAT_CIRCLE;
-#ifdef USE_MPF
-        yac_mpf_init_vector(circle->data.gc.norm_vector);
-#endif
-        compute_norm_vector_ld(a, b, circle->data.gc.norm_vector);
+        compute_norm_vector_kahan(a, b, circle->data.gc.norm_vector);
         break;
       case(LON_CIRCLE_EDGE):
         circle->type = LON_CIRCLE;
-#ifdef USE_MPF
-        yac_mpf_init_vector(circle->data.lon.norm_vector);
-#endif
-        compute_norm_vector_ld(a, b, circle->data.lon.norm_vector);
+        compute_norm_vector_kahan(a, b, circle->data.lon.norm_vector);
         break;
       case(LAT_CIRCLE_EDGE):
         circle->type = LAT_CIRCLE;
@@ -586,23 +456,6 @@ static inline struct yac_circle
   return circle;
 }
 
-#ifdef USE_MPF
-void yac_circles_free(struct yac_circle* circles, size_t size) {
-  for (size_t i = 0; i < size; ++i) {
-    switch (circles[i].type) {
-      case(GREAT_CIRCLE):
-        yac_mpf_clear_vector(circles[i].data.gc.norm_vector);
-        break;
-      case(LON_CIRCLE):
-        yac_mpf_clear_vector(circles[i].data.lon.norm_vector);
-        break;
-      default:
-        break;
-    }
-  }
-}
-#endif
-
 int yac_circle_point_is_inside(
   double const point[3], struct yac_circle * circle) {
 
@@ -616,29 +469,17 @@ int yac_circle_point_is_inside(
   switch (circle->type) {
     default:
     case (GREAT_CIRCLE): {
-#ifndef USE_MPF
-      double dot = point[0] * (double)(circle->data.gc.norm_vector[0]) +
-                   point[1] * (double)(circle->data.gc.norm_vector[1]) +
-                   point[2] * (double)(circle->data.gc.norm_vector[2]);
-#else
-      double dot = point[0] * mpf_get_d(circle->data.gc.norm_vector[0]) +
-                   point[1] * mpf_get_d(circle->data.gc.norm_vector[1]) +
-                   point[2] * mpf_get_d(circle->data.gc.norm_vector[2]);
-#endif
+      double dot = point[0] * circle->data.gc.norm_vector[0] +
+                   point[1] * circle->data.gc.norm_vector[1] +
+                   point[2] * circle->data.gc.norm_vector[2];
       if      (dot < (- yac_angle_tol * 1e-3)) return 0;
       else if (dot > (+ yac_angle_tol * 1e-3)) return 1;
       else return 2;
     }
     case (LON_CIRCLE): {
-#ifndef USE_MPF
-      double dot = point[0] * (double)(circle->data.lon.norm_vector[0]) +
-                   point[1] * (double)(circle->data.lon.norm_vector[1]) +
-                   point[2] * (double)(circle->data.lon.norm_vector[2]);
-#else
-      double dot = point[0] * mpf_get_d(circle->data.lon.norm_vector[0]) +
-                   point[1] * mpf_get_d(circle->data.lon.norm_vector[1]) +
-                   point[2] * mpf_get_d(circle->data.lon.norm_vector[2]);
-#endif
+      double dot = point[0] * circle->data.lon.norm_vector[0] +
+                   point[1] * circle->data.lon.norm_vector[1] +
+                   point[2] * circle->data.lon.norm_vector[2];
       if      (dot < (- yac_angle_tol * 1e-3)) return 0;
       else if (dot > (+ yac_angle_tol * 1e-3)) return 1;
       else return 2;
@@ -667,15 +508,9 @@ int yac_circle_compare_distances(
   switch (circle->type) {
     default:
     case(GREAT_CIRCLE): {
-#ifndef USE_MPF
-      double norm_vector[3] = {(double)(circle->data.gc.norm_vector[0]),
-                               (double)(circle->data.gc.norm_vector[1]),
-                               (double)(circle->data.gc.norm_vector[2])};
-#else
-      double norm_vector[3] = {mpf_get_d(circle->data.gc.norm_vector[0]),
-                               mpf_get_d(circle->data.gc.norm_vector[1]),
-                               mpf_get_d(circle->data.gc.norm_vector[2])};
-#endif
+      double norm_vector[3] = {circle->data.gc.norm_vector[0],
+                               circle->data.gc.norm_vector[1],
+                               circle->data.gc.norm_vector[2]};
       dist_a = fabs(a[0] * norm_vector[0] +
                     a[1] * norm_vector[1] +
                     a[2] * norm_vector[2]);
@@ -685,15 +520,9 @@ int yac_circle_compare_distances(
       break;
     }
     case(LON_CIRCLE): {
-#ifndef USE_MPF
-      double norm_vector[3] = {(double)(circle->data.lon.norm_vector[0]),
-                               (double)(circle->data.lon.norm_vector[1]),
-                               (double)(circle->data.lon.norm_vector[2])};
-#else
-      double norm_vector[3] = {mpf_get_d(circle->data.lon.norm_vector[0]),
-                               mpf_get_d(circle->data.lon.norm_vector[1]),
-                               mpf_get_d(circle->data.lon.norm_vector[2])};
-#endif
+      double norm_vector[3] = {circle->data.lon.norm_vector[0],
+                               circle->data.lon.norm_vector[1],
+                               circle->data.lon.norm_vector[2]};
       dist_a = fabs(a[0] * norm_vector[0] +
                     a[1] * norm_vector[1] +
                     a[2] * norm_vector[2]);
@@ -731,11 +560,7 @@ int yac_circle_contains_north_pole(struct yac_circle * circle) {
   switch (circle->type) {
     default:
     case (GREAT_CIRCLE):
-#ifndef USE_MPF
       return circle->data.gc.norm_vector[2] > 0.0;
-#else
-      return mpf_sgn(circle->data.gc.norm_vector[2]) > 0;
-#endif
     case (LAT_CIRCLE):
       return !circle->data.lat.north_is_out;
     case (LON_CIRCLE):
@@ -884,28 +709,19 @@ static void circle_clipping(
                 cell_edge_coords[1][0],
                 cell_edge_coords[1][1],
                 cell_edge_coords[1][2], (int)cell_edge_circle->type,
-#ifndef USE_MPF
-                (double)(clipping_circle->data.gc.norm_vector[0]),
-                (double)(clipping_circle->data.gc.norm_vector[1]),
-                (double)(clipping_circle->data.gc.norm_vector[2]),
-                (double)(clipping_circle->data.lon.norm_vector[0]),
-                (double)(clipping_circle->data.lon.norm_vector[1]),
-                (double)(clipping_circle->data.lon.norm_vector[2]),
-#else
-                mpf_get_d(clipping_circle->data.gc.norm_vector[0]),
-                mpf_get_d(clipping_circle->data.gc.norm_vector[1]),
-                mpf_get_d(clipping_circle->data.gc.norm_vector[2]),
-                mpf_get_d(clipping_circle->data.lon.norm_vector[0]),
-                mpf_get_d(clipping_circle->data.lon.norm_vector[1]),
-                mpf_get_d(clipping_circle->data.lon.norm_vector[2]),
-#endif
-                clipping_circle->data.lat.z,
-                clipping_circle->data.lat.north_is_out,
-                clipping_circle->data.p.vec[0],
-                clipping_circle->data.p.vec[1],
-                clipping_circle->data.p.vec[2], (int)(clipping_circle->type),
-                intersection[0][0], intersection[0][1], intersection[0][2],
-                intersection[1][0], intersection[1][1], intersection[1][2])
+                  clipping_circle->data.gc.norm_vector[0],
+                  clipping_circle->data.gc.norm_vector[1],
+                  clipping_circle->data.gc.norm_vector[2],
+                  clipping_circle->data.lon.norm_vector[0],
+                  clipping_circle->data.lon.norm_vector[1],
+                  clipping_circle->data.lon.norm_vector[2],
+                  clipping_circle->data.lat.z,
+                  clipping_circle->data.lat.north_is_out,
+                  clipping_circle->data.p.vec[0],
+                  clipping_circle->data.p.vec[1],
+                  clipping_circle->data.p.vec[2], (int)(clipping_circle->type),
+                  intersection[0][0], intersection[0][1], intersection[0][2],
+                  intersection[1][0], intersection[1][1], intersection[1][2])
 
               // if both cell edge vertices are on the same side of the
               // clipping edge
@@ -1359,11 +1175,7 @@ void yac_cell_clipping (size_t N,
 
     generate_cell(overlap, overlap_buffer + n);
 
-    yac_circles_free(src_circle_buffer, source_cell[n].num_corners);
   }
-
-  yac_circles_free(circle_buffer, target_cell.num_corners);
-
   free(circle_buffer);
   free_point_list(&source_list);
   free_point_list(&target_list);
@@ -1611,8 +1423,6 @@ void yac_cell_lat_clipping (size_t N,
       circle_clipping(&cell_list, num_corners, lat_circles, num_lat_circles);
 
       generate_cell(&cell_list, overlap_buffer + n);
-
-      yac_circles_free(circle_buffer, cells[n].num_corners);
     }
   }
 
@@ -1884,7 +1694,7 @@ static void free_point_list(struct point_list * list) {
 
 static void compute_norm_vector(double a[], double b[], double norm[]) {
 
-  crossproduct_ld(a, b, norm);
+  crossproduct_kahan(a, b, norm);
 
   YAC_ASSERT(
     (fabs(norm[0]) >= tol) || (fabs(norm[1]) >= tol) || (fabs(norm[2]) >= tol),
diff --git a/src/lib/yac/clipping.h b/src/lib/yac/clipping.h
index 02c0dc382699baf8a7a79496bbe40d18b230f211..660bd4b5432d943bc16e65f8824a5318e3632646 100644
--- a/src/lib/yac/clipping.h
+++ b/src/lib/yac/clipping.h
@@ -1,49 +1,6 @@
-/**
- * @file clipping.h
- * @brief Structs and interfaces for cell clipping
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifndef CLIPPING_H
 #define CLIPPING_H
@@ -208,18 +165,6 @@ void yac_circle_generate(
   double const * a, double const * b, enum yac_edge_type type,
   int edge_ordering, struct yac_circle * circle);
 
-#ifndef USE_MPF
-#define yac_circles_free(circles, size)
-#else
-/**
- * Frees memory allocated by an array of yac circles
- *
- * @param[in] circles yac circles
- * @param[in] size    number of entries in circles
- */
-void yac_circles_free(struct yac_circle * circles, size_t size);
-#endif
-
 /**
  * Compare routine for yac circles (first by type and second parameters
  *                                  of the circle, if types are identical)
diff --git a/src/lib/yac/compare_files b/src/lib/yac/compare_files
index ab71dd1401f28b2ec9dbf5d9e55580e24c96f6a4..ab7735010176bed0123dce2fcca05815039077f0 100755
--- a/src/lib/yac/compare_files
+++ b/src/lib/yac/compare_files
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-YACSRC=/Users/m214003/cdt/work/YAC/YAC-dev/src
+YACSRC=/Users/m214003/cdt/work/YAC/YAC-dev/src/core
 #
 FILES="*.c *.h"
 #
diff --git a/src/lib/yac/ensure_array_size.c b/src/lib/yac/ensure_array_size.c
index 88dde7a346185ae45da40f12136364f979795c00..475bffd4ecb8c655eb20b72218864a056c72ca38 100644
--- a/src/lib/yac/ensure_array_size.c
+++ b/src/lib/yac/ensure_array_size.c
@@ -1,51 +1,12 @@
-/**
- * @file ensure_array_size.c
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "ensure_array_size.h"
-#include "utils.h"
+#include "utils_core.h"
 
 void
 yac_realloc_array(void **array, size_t elem_size, size_t *curr_array_size,
diff --git a/src/lib/yac/ensure_array_size.h b/src/lib/yac/ensure_array_size.h
index d98a03daa0bde55f7c422677555b1c77f712d14f..85e54289abebb9d28b4fadb2b2942a3974c6e91f 100644
--- a/src/lib/yac/ensure_array_size.h
+++ b/src/lib/yac/ensure_array_size.h
@@ -1,48 +1,6 @@
-/**
- * @file ensure_array_size.h
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Thomas Jahns <jahns@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Thomas Jahns <jahns@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Thomas Jahns <jahns@dkrz.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifndef ENSURE_ARRAY_SIZE_H
 #define ENSURE_ARRAY_SIZE_H
diff --git a/src/lib/yac/field_data.h b/src/lib/yac/field_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..2eedcbc3b3a28a934779aa8a502057f52609b3c8
--- /dev/null
+++ b/src/lib/yac/field_data.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef FIELD_DATA_H
+#define FIELD_DATA_H
+
+#include "yac_types.h"
+
+struct yac_field_data;
+
+struct yac_field_data * yac_field_data_empty_new();
+size_t yac_field_data_add_mask_nocpy(
+  struct yac_field_data * field_data, int const * mask,
+  char const * mask_name);
+size_t yac_field_data_add_coordinates_nocpy(
+  struct yac_field_data * field_data, yac_coordinate_pointer coordinates);
+size_t yac_field_data_get_masks_count(struct yac_field_data * field_data);
+int const * yac_field_data_get_mask_data(
+  struct yac_field_data * field_data, size_t mask_idx);
+void yac_field_data_set_mask_data(
+  struct yac_field_data * field_data, size_t mask_idx, int * mask_data);
+char const * yac_field_data_get_mask_name(
+  struct yac_field_data * field_data, size_t mask_idx);
+size_t yac_field_data_get_coordinates_count(struct yac_field_data * field_data);
+yac_const_coordinate_pointer yac_field_data_get_coordinates_data(
+  struct yac_field_data * field_data, size_t coordinates_idx);
+void yac_field_data_set_coordinates_data(
+  struct yac_field_data * field_data, size_t coordinates_idx,
+  yac_coordinate_pointer coordinates_data);
+void yac_field_data_delete(struct yac_field_data * field_data);
+
+#endif // FIELD_DATA_H
diff --git a/src/lib/yac/geometry.h b/src/lib/yac/geometry.h
index 72bd2f3f6b6696480a5a85058ee76760e285a552..339f68c5dfffb7496df391ebdfe8608b3134e73a 100644
--- a/src/lib/yac/geometry.h
+++ b/src/lib/yac/geometry.h
@@ -1,55 +1,6 @@
-/**
- * @file geometry.h
- * @brief Structs and interfaces for investigating the geometry and relations of cells
- *
- * The functions are used to determine relations between
- * source and target cells. Complete cells are constructed
- * on the sphere by connecting cell vertices along either
- * great circles (along the orthodrome) or directly (along
- * the loxodrome).
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 /** \example test_geometry.c
  * This contains some tests for basic routines of \ref geometry.h.
@@ -72,17 +23,11 @@
 #include <math.h>
 #include <float.h>
 
-#include "yac_ld_interface.h"
-#include "grid.h"
-#include "utils.h"
+#include "basic_grid.h"
+#include "grid_cell.h"
+#include "utils_core.h"
 
-#ifndef M_SQRT1_2
-#define M_SQRT1_2 (0.707106781186547524401) /* sqrt(0.5) */
-#endif
-
-#ifndef M_SQRT3_4
-#define M_SQRT3_4 (0.866025403784438646764) /* sqrt(0.75) */
-#endif
+#define YAC_RAD (0.01745329251994329576923690768489) // M_PI / 180
 
 // angle tolerance
 
@@ -138,11 +83,7 @@ struct yac_circle {
   union {
     struct {
       // the norm vector points into the inside direction
-#ifndef USE_MPF
-      long double norm_vector[3];
-#else
-      mpf_t norm_vector[3];
-#endif
+      double norm_vector[3];
     } gc, lon;
     struct {
       int north_is_out;
@@ -192,6 +133,22 @@ static inline double get_angle (double a_lon, double b_lon) {
 #endif
 }
 
+/**
+ * computes the angle between two longitude coordinates (in deg) \n
+ * takes into account that longitude have a period of 360
+ * @param[in] a_lon
+ * @param[in] b_lon
+ * @return angle between both coordinates (in rad)
+ */
+static inline double get_angle_deg (double a_lon, double b_lon) {
+   double diff = a_lon - b_lon;
+#if defined(CDO)
+   return diff - lround(diff / 360.0) * (360.0);
+#else
+   return diff - round(diff / 360.0) * (360.0);
+#endif
+}
+
 /**
  * computes the intersection points of two edges
  * @param[in]  edge_type_a type of edge a
@@ -364,55 +321,28 @@ static inline void XYZtoLL (double const p_in[], double * lon, double * lat) {
    *lat = M_PI_2 - acos(p_in[2]);
 }
 
-#ifndef USE_MPF
-static inline void crossproduct_ld (
-  double const a[], double const b[], double cross[]) {
-
-/* crossproduct in Cartesian coordinates */
-
-   long double a_[3] = {a[0], a[1], a[2]};
-   long double b_[3] = {b[0], b[1], b[2]};
-
-   cross[0] = (double)(a_[1] * b_[2] - a_[2] * b_[1]);
-   cross[1] = (double)(a_[2] * b_[0] - a_[0] * b_[2]);
-   cross[2] = (double)(a_[0] * b_[1] - a_[1] * b_[0]);
+// computation of the determinant of a 2x2 matrix using Kahan summation
+static inline double det2_kahan(double a, double b, double c, double d) {
+  double bc = b*c;
+  double e_bc = fma(b,c,-bc); // the rounding error of the multiplication
+  double det = fma(a,d,-bc);
+  return det + e_bc;
 }
-#else
-static void crossproduct_ld (
-  double const a[], double const b[], double cross[]) {
 
-   mpf_t a_[3], b_[3], cross_[3];
-   yac_mpf_set_vector_d(a_, a);
-   yac_mpf_set_vector_d(b_, b);
-   yac_mpf_init_vector(cross_);
-
-   mpf_mul(cross_[0], a_[1], b_[2]);
-   mpf_mul(cross_[1], a_[2], b_[0]);
-   mpf_mul(cross_[2], a_[0], b_[1]);
-
-   mpf_t buf;
-   mpf_init2(buf, YAC_MPF_PRECISION);
+static inline void crossproduct_kahan (
+  double const a[], double const b[], double cross[]) {
 
-   mpf_mul(buf, a_[2], b_[1]);
-   mpf_sub(cross_[0], cross_[0], buf);
-   mpf_mul(buf, a_[0], b_[2]);
-   mpf_sub(cross_[1], cross_[1], buf);
-   mpf_mul(buf, a_[1], b_[0]);
-   mpf_sub(cross_[2], cross_[2], buf);
+  /* crossproduct in Cartesian coordinates */
 
-   cross[0] = mpf_get_d(cross_[0]);
-   cross[1] = mpf_get_d(cross_[1]);
-   cross[2] = mpf_get_d(cross_[2]);
+  cross[0] = det2_kahan(a[1], a[2], b[1], b[2]);
+  cross[1] = det2_kahan(a[2], a[0], b[2], b[0]);
+  cross[2] = det2_kahan(a[0], a[1], b[0], b[1]);
 
-   yac_mpf_clear_vector(a_);
-   yac_mpf_clear_vector(b_);
-   yac_mpf_clear_vector(cross_);
 }
-#endif
 
 /**
  * for small angles <= 1e-?8? the crossproduct is inaccurate\n
- * use \ref crossproduct_ld for these cases
+ * use \ref crossproduct_kahan for these cases
  */
 static inline void crossproduct_d (
   const double a[], const double b[], double cross[]) {
@@ -440,7 +370,7 @@ static inline double get_vector_angle(double const a[3], double const b[3]) {
 
       double cross_ab[3];
 
-      crossproduct_ld(a, b, cross_ab);
+      crossproduct_kahan(a, b, cross_ab);
 
       double asin_tmp = asin(sqrt(cross_ab[0]*cross_ab[0]+
                                   cross_ab[1]*cross_ab[1]+
@@ -460,7 +390,7 @@ static inline double get_vector_angle(double const a[3], double const b[3]) {
    // this solution is simpler, but has a much worse performance
    double cross[3], dot, cross_abs;
 
-   crossproduct_ld(a, b, cross);
+   crossproduct_kahan(a, b, cross);
    dot = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
    cross_abs = sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]);
 
@@ -483,10 +413,10 @@ static inline struct sin_cos_angle sin_cos_angle_new(double sin, double cos) {
 }
 
 static inline struct sin_cos_angle get_vector_angle_2(
-  double a[3], double b[3]) {
+  double const a[3], double const b[3]) {
 
   double cross_ab[3];
-  crossproduct_ld(a, b, cross_ab);
+  crossproduct_kahan(a, b, cross_ab);
 
   return sin_cos_angle_new(sqrt(cross_ab[0]*cross_ab[0] +
                                 cross_ab[1]*cross_ab[1] +
@@ -741,7 +671,7 @@ static inline int compare_coords(double const * a, double const * b) {
      // accuracy
 
       double cross_ab[3];
-      crossproduct_ld(a, b, cross_ab);
+      crossproduct_kahan(a, b, cross_ab);
 
       // for very small angles: asin(alpha) = ~alpha   (alpha in rad)
       if (sqrt(cross_ab[0]*cross_ab[0] +
diff --git a/src/lib/yac/grid.h b/src/lib/yac/grid.h
deleted file mode 100644
index fe76627eea6bfbaae9df6d0bec7e0c37e49191af..0000000000000000000000000000000000000000
--- a/src/lib/yac/grid.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/**
- * @file grid.h
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef GRID_H
-#define GRID_H
-
-#include "utils.h"
-#include "grid_cell.h"
-
-#define YAC_MAX_LOC_STR_LEN 10
-
-enum yac_location {
-
-   CELL =   0,
-   CORNER = 1,
-   EDGE =   2,
-   LOC_UNDEFINED = 3,
-};
-
-/** \example test_grid.c
- */
-
-#ifndef  M_PI
-#define  M_PI        3.14159265358979323846264338327950288  /* pi */
-#endif
-
-#ifndef  M_PI_2
-#define  M_PI_2      1.57079632679489661923132169163975144  /* pi/2 */
-#endif
-
-#define YAC_EARTH_RADIUS (6371.2290)
-#define YAC_EARTH_RADIUS2 ((6371.2290 * 6371.2290) * 0.5)
-#define YAC_RAD (0.01745329251994329576923690768489) // M_PI / 180
-
-typedef size_t (* size_t_2_pointer)[2];
-
-struct yac_field_data {
-  int ** masks;
-  size_t masks_count;
-  coordinate_pointer * coordinates;
-  size_t coordinates_count;
-};
-
-struct yac_field_data_set {
-  struct yac_field_data cell, vertex, edge;
-};
-
-struct interp_field {
-  enum yac_location location;
-  size_t coordinates_idx;
-  size_t masks_idx;
-};
-
-struct basic_grid_data {
-  coordinate_pointer vertex_coordinates;
-  yac_int * cell_ids;
-  yac_int * vertex_ids;
-  yac_int * edge_ids;
-  size_t num_cells; // number of local cells (owned by local process)
-  size_t num_vertices; // number of local vertices (owned by local process)
-  size_t num_edges; // number of local edges (owned by local process)
-  int * core_cell_mask;
-  int * core_vertex_mask;
-  int * core_edge_mask;
-  int * num_vertices_per_cell;
-  int * num_cells_per_vertex;
-  size_t * cell_to_vertex;
-  size_t * cell_to_vertex_offsets;
-  size_t * cell_to_edge;
-  size_t * cell_to_edge_offsets;
-  size_t * vertex_to_cell;
-  size_t * vertex_to_cell_offsets;
-  size_t_2_pointer edge_to_vertex;
-  enum yac_edge_type * edge_type;
-  size_t num_total_cells; // number of locally stored cells
-  size_t num_total_vertices; // number of locally stored vertices
-  size_t num_total_edges; // number of locally stored edges
-};
-
-struct yac_basic_grid;
-
-struct basic_grid_data yac_generate_basic_grid_data_reg_2d(
-  size_t nbr_vertices[2], int cyclic[2],
-  double *lon_vertices, double *lat_vertices);
-
-struct basic_grid_data yac_generate_basic_grid_data_reg_2d_deg(
-  size_t nbr_vertices[2], int cyclic[2],
-  double *lon_vertices, double *lat_vertices);
-
-struct basic_grid_data yac_generate_basic_grid_data_curve_2d(
-  size_t nbr_vertices[2], int cyclic[2],
-  double *lon_vertices, double *lat_vertices);
-
-struct basic_grid_data yac_generate_basic_grid_data_curve_2d_deg(
-  size_t nbr_vertices[2], int cyclic[2],
-  double *lon_vertices, double *lat_vertices);
-
-struct basic_grid_data yac_generate_basic_grid_data_unstruct(
-  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
-  double *x_vertices, double *y_vertices, int *cell_to_vertex);
-
-struct basic_grid_data yac_generate_basic_grid_data_unstruct_deg(
-  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
-  double *x_vertices, double *y_vertices, int *cell_to_vertex);
-
-struct basic_grid_data yac_generate_basic_grid_data_unstruct_ll(
-  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
-  double *x_vertices, double *y_vertices, int *cell_to_vertex);
-
-struct basic_grid_data yac_generate_basic_grid_data_unstruct_ll_deg(
-  size_t nbr_vertices, size_t nbr_cells, int *num_vertices_per_cell,
-  double *x_vertices, double *y_vertices, int *cell_to_vertex);
-
-struct yac_basic_grid * yac_basic_grid_new(
-  char const * name, struct basic_grid_data grid_data);
-struct yac_basic_grid * yac_basic_grid_empty_new(char const * name);
-coordinate_pointer yac_basic_grid_get_field_coordinates(
-  struct yac_basic_grid * grid, struct interp_field field);
-int const * yac_basic_grid_get_field_mask(
-  struct yac_basic_grid * grid, struct interp_field field);
-int const * yac_basic_grid_get_core_mask(
-  struct yac_basic_grid * grid, enum yac_location location);
-char const * yac_basic_grid_get_name(struct yac_basic_grid * grid);
-struct basic_grid_data * yac_basic_grid_get_data(struct yac_basic_grid * grid);
-struct yac_field_data * yac_basic_grid_get_field_data(
-  struct yac_basic_grid * grid, enum yac_location location);
-size_t yac_basic_grid_get_data_size(
-  struct yac_basic_grid * grid, enum yac_location location);
-size_t yac_basic_grid_add_coordinates(
-  struct yac_basic_grid * grid, enum yac_location location,
-  coordinate_pointer coordinates, size_t count);
-size_t yac_basic_grid_add_coordinates_nocpy(
-  struct yac_basic_grid * grid, enum yac_location location,
-  coordinate_pointer coordinates);
-size_t yac_basic_grid_add_mask(
-  struct yac_basic_grid * grid, enum yac_location location,
-  int const * mask, size_t count);
-size_t yac_basic_grid_add_mask_nocpy(
-  struct yac_basic_grid * grid, enum yac_location location,
-  int const * mask);
-void yac_basic_grid_delete(struct yac_basic_grid * grid);
-
-void yac_basic_grid_data_free(struct basic_grid_data grid);
-
-enum yac_location yac_str2loc(char const * location);
-char const * yac_loc2str(enum yac_location location);
-enum yac_location yac_get_location(int const location);
-
-struct yac_field_data yac_field_data_init();
-size_t yac_field_data_add_mask_nocpy(
-  struct yac_field_data * field_data, int const * mask);
-size_t yac_field_data_add_coordinates_nocpy(
-  struct yac_field_data * field_data, coordinate_pointer coordinates);
-
-struct yac_field_data_set yac_field_data_set_init();
-size_t yac_field_data_set_add_mask(
-  struct yac_field_data_set * field_data_set,
-  enum yac_location location, int const * mask, size_t count);
-size_t yac_field_data_set_add_coordinates(
-  struct yac_field_data_set * field_data_set,
-  enum yac_location location, coordinate_pointer coordinates,
-  size_t count);
-size_t yac_field_data_set_add_mask_nocpy(
-  struct yac_field_data_set * field_data_set,
-  enum yac_location location, int const * mask);
-size_t yac_field_data_set_add_coordinates_nocpy(
-  struct yac_field_data_set * field_data_set,
-  enum yac_location location, coordinate_pointer coordinates);
-void yac_field_data_set_free(struct yac_field_data_set field_data_set);
-
-#endif // GRID_H
-
diff --git a/src/lib/yac/grid_cell.c b/src/lib/yac/grid_cell.c
index e571c43256f38bf32c0097a1c3e27d2103982332..0f536d7fbef208f2d117bf116f32c1d4856d8dc3 100644
--- a/src/lib/yac/grid_cell.c
+++ b/src/lib/yac/grid_cell.c
@@ -1,58 +1,13 @@
-/**
- * @file grid_cell.c
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *                                 Thomas Jahns <jahns@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- *         Thomas Jahns <jahns@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- *             Thomas Jahns <jahns@dkrz.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 
 #include "grid_cell.h"
-#include "utils.h"
+#include "utils_core.h"
 #include "ensure_array_size.h"
 #include "geometry.h"
 
diff --git a/src/lib/yac/grid_cell.h b/src/lib/yac/grid_cell.h
index d90aaa0dd24cb402f43f4c843f38acc5eb08376d..46e0980b1b8bceb9dd95682f9bb59a8cec3d2955 100644
--- a/src/lib/yac/grid_cell.h
+++ b/src/lib/yac/grid_cell.h
@@ -1,49 +1,6 @@
-/**
- * @file grid_cell.h
- * @brief Structs and interfaces to handle grid cells
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdio.h>
 
diff --git a/src/lib/yac/intersection.c b/src/lib/yac/intersection.c
index 63227accdd65c1d9da6f1005e7117720c207691b..d4478695848da2d4e9e78867d0a5d6c0879d088d 100644
--- a/src/lib/yac/intersection.c
+++ b/src/lib/yac/intersection.c
@@ -1,66 +1,16 @@
-/**
- * @file intersection.c
- * @brief Set of functions to determine the intersection between two edges
- *
- * very interesting literature:
- * - http://geospatialmethods.org/spheres/GCIntersect.html
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdlib.h>
 #include <math.h>
 #include <stdio.h>
 
-#include "yac_ld_interface.h"
-#include "utils.h"
+#include "utils_core.h"
 #include "geometry.h"
 
 // angle tolerance
 static double const tol = 1.0e-12;
-#ifndef USE_MPF
-static long double const tol_ld = 1.0e-12;
-#endif
 
 enum {
   p_on_a = 1,
@@ -86,94 +36,31 @@ enum {
  * @param[out] q             second intersection point
  * @return -1 if the two circles are identical; 2 otherwise
  */
-#ifndef USE_MPF
 static int gcxgc(
-  long double const norm_vector_a[3], long double const norm_vector_b[3],
+  double const norm_vector_a[3], double const norm_vector_b[3],
   double p[3], double q[3]) {
 
   // compute p and q
   // p' = (a X b) X (c X d)
   // p  = p' / length(p')
   // q = -p
-  long double temp_p[3] =
-    {norm_vector_a[1] * norm_vector_b[2] - norm_vector_a[2] * norm_vector_b[1],
-     norm_vector_a[2] * norm_vector_b[0] - norm_vector_a[0] * norm_vector_b[2],
-     norm_vector_a[0] * norm_vector_b[1] - norm_vector_a[1] * norm_vector_b[0]};
+  double temp_p[3];
+  crossproduct_kahan(norm_vector_a, norm_vector_b, temp_p);
 
-  long double sq_sin_plane_angle =
+  double sq_sin_plane_angle =
     temp_p[0] * temp_p[0] + temp_p[1] * temp_p[1] + temp_p[2] * temp_p[2];
 
   // if both circles are on the same plane
   if (sq_sin_plane_angle <= yac_sq_angle_tol) return -1;
 
-  long double scale = 1.0 / sqrtl(sq_sin_plane_angle);
-  p[0] = (double)(temp_p[0] * scale);
-  p[1] = (double)(temp_p[1] * scale);
-  p[2] = (double)(temp_p[2] * scale);
+  double scale = 1.0 / sqrt(sq_sin_plane_angle);
+  p[0] = (temp_p[0] * scale);
+  p[1] = (temp_p[1] * scale);
+  p[2] = (temp_p[2] * scale);
   q[0] = -p[0], q[1] = -p[1], q[2] = -p[2];
 
   return 2;
 }
-#else
-static int gcxgc(
-  mpf_t const norm_vector_a[3], mpf_t const norm_vector_b[3],
-  double p[3], double q[3]) {
-
-  mpf_t temp_p[3];
-  yac_mpf_init_vector(temp_p);
-
-  mpf_mul(temp_p[0], norm_vector_a[1], norm_vector_b[2]);
-  mpf_mul(temp_p[1], norm_vector_a[2], norm_vector_b[0]);
-  mpf_mul(temp_p[2], norm_vector_a[0], norm_vector_b[1]);
-
-  mpf_t buf;
-  mpf_init2(buf, YAC_MPF_PRECISION);
-
-  mpf_mul(buf, norm_vector_a[2], norm_vector_b[1]);
-  mpf_sub(temp_p[0], temp_p[0], buf);
-  mpf_mul(buf, norm_vector_a[0], norm_vector_b[2]);
-  mpf_sub(temp_p[1], temp_p[1], buf);
-  mpf_mul(buf, norm_vector_a[1], norm_vector_b[0]);
-  mpf_sub(temp_p[2], temp_p[2], buf);
-
-  mpf_t sq_sin_plane_angle;
-  mpf_init2(sq_sin_plane_angle, YAC_MPF_PRECISION);
-  mpf_mul(sq_sin_plane_angle, temp_p[0], temp_p[0]);
-  mpf_mul(buf, temp_p[1], temp_p[1]);
-  mpf_add(sq_sin_plane_angle, sq_sin_plane_angle, buf);
-  mpf_mul(buf, temp_p[2], temp_p[2]);
-  mpf_add(sq_sin_plane_angle, sq_sin_plane_angle, buf);
-
-  int ret;
-
-  // if both circles are on the same plane
-  if (mpf_cmp_d(sq_sin_plane_angle, yac_sq_angle_tol) <= 0) {
-    ret = -1;
-  } else {
-    // reuse sq_sin_plane_angle to hold its square root
-    mpf_sqrt(sq_sin_plane_angle, sq_sin_plane_angle);
-    // reuse buf to hold the scale
-    mpf_ui_div(buf, 1, sq_sin_plane_angle);
-    // scale temp_p in place
-    mpf_mul(temp_p[0], temp_p[0], buf);
-    mpf_mul(temp_p[1], temp_p[1], buf);
-    mpf_mul(temp_p[2], temp_p[2], buf);
-
-    p[0] = mpf_get_d(temp_p[0]);
-    p[1] = mpf_get_d(temp_p[1]);
-    p[2] = mpf_get_d(temp_p[2]);
-    q[0] = -p[0], q[1] = -p[1], q[2] = -p[2];
-    ret = 2;
-  }
-
-  yac_mpf_clear_vector(temp_p);
-  mpf_clear(buf);
-  mpf_clear(sq_sin_plane_angle);
-  return ret;
-}
-#endif
-
-#ifndef USE_MPF
 
 #if defined __INTEL_COMPILER
 #pragma intel optimization_level 0
@@ -182,20 +69,20 @@ static int gcxgc(
 #endif
 
 static int loncxlatc(
-  long double const gc_norm_vector[3], double const z,
+  double const gc_norm_vector[3], double const z,
   double p[3], double q[3]) {
 
   // based on gcxlatc with some optimisations for circles of longitude
 
   // square of z of lat circle
-  long double sq_z = (long double)(z * z);
+  double sq_z = z * z;
 
-  long double b = sqrt(MAX(1.0 - sq_z,0.0));
-  long double b_n0 = b * gc_norm_vector[0];
-  long double b_n1 = b * gc_norm_vector[1];
+  double b = sqrt(MAX(1.0 - sq_z,0.0));
+  double b_n0 = b * gc_norm_vector[0];
+  double b_n1 = b * gc_norm_vector[1];
 
-  p[0] = (double)(+ b_n1);
-  p[1] = (double)(- b_n0);
+  p[0] = b_n1;
+  p[1] = -b_n0;
   p[2] = z;
   q[0] = -p[0];
   q[1] = -p[1];
@@ -204,66 +91,8 @@ static int loncxlatc(
   return 2;
 }
 
-#if defined _CRAYC
-#pragma _CRI opt
-#endif
-
-#else
-static int loncxlatc(
-  mpf_t const gc_norm_vector[3], double const z,
-  double p[3], double q[3]) {
-
-  mpf_t buf;
-  mpf_init2(buf, YAC_MPF_PRECISION);
-  // buf = z
-  mpf_set_d(buf, z);
-  // buf *= buf
-  mpf_mul(buf, buf, buf);
-  // buf = 1.0 / buf
-  mpf_ui_sub(buf, 1, buf);
-
-  if (mpf_sgn(buf) > 0) {
-    mpf_sqrt(buf, buf);
-
-    mpf_t b;
-    mpf_init2(b, YAC_MPF_PRECISION);
-
-    mpf_mul(b, buf, gc_norm_vector[1]);
-    p[0] = mpf_get_d(b);
-
-    mpf_mul(b, buf, gc_norm_vector[0]);
-    mpf_neg(b, b);
-    p[1] = mpf_get_d(b);
-
-    p[2] = z;
-    q[0] = -p[0];
-    q[1] = -p[1];
-    q[2] = z;
-
-    mpf_clear(b);
-  } else {
-    p[0] = 0.0;
-    p[1] = 0.0;
-    p[2] = z;
-    q[0] = 0.0;
-    q[1] = 0.0;
-    q[2] = z;
-  }
-
-  mpf_clear(buf);
-
-  return 2;
-}
-#endif
-
-#ifndef USE_MPF
-
-#if defined _CRAYC
-#pragma _CRI noopt
-#endif
-
 static int gcxlatc(
-  long double const gc_norm_vector[3], double const z,
+  double const gc_norm_vector[3], double const z,
   double p[3], double q[3]) {
 
 /* How to compute intersection point(s) between great circle (given by its
@@ -301,22 +130,22 @@ static int gcxlatc(
   if (fabsl(gc_norm_vector[2]) < yac_angle_low_tol)
      return loncxlatc(gc_norm_vector, z, p, q);
 
-  long double sq_n[3] = {gc_norm_vector[0] * gc_norm_vector[0],
-                         gc_norm_vector[1] * gc_norm_vector[1],
-                         gc_norm_vector[2] * gc_norm_vector[2]};
+  double sq_n[3] = {gc_norm_vector[0] * gc_norm_vector[0],
+                    gc_norm_vector[1] * gc_norm_vector[1],
+                    gc_norm_vector[2] * gc_norm_vector[2]};
 
   // square of highest z-value of great circle
-  long double max_sq_gc_z = sq_n[0] + sq_n[1];
+  double max_sq_gc_z = sq_n[0] + sq_n[1];
 
   // square of z of lat circle
-  long double sq_z = (long double)(z * z);
+  double sq_z = z * z;
 
   // if both circles are the equator -> identical circles
   if ((max_sq_gc_z < yac_sq_angle_tol) && (sq_z < yac_sq_angle_tol)) return -1;
 
   // determine number of intersections
   int num_intersections =
-    ((max_sq_gc_z > (sq_z - tol_ld)) - (max_sq_gc_z < (sq_z + tol_ld))) + 1;
+    ((max_sq_gc_z > (sq_z - tol)) - (max_sq_gc_z < (sq_z + tol))) + 1;
 
   switch (num_intersections) {
 
@@ -327,10 +156,10 @@ static int gcxlatc(
     // ==> (b = 0)
     case (1): {
 
-      long double a = -(gc_norm_vector[2] * z) / max_sq_gc_z;
+      double a = -(gc_norm_vector[2] * z) / max_sq_gc_z;
 
-      p[0] = ((q[0] = (double)(gc_norm_vector[0] * a)));
-      p[1] = ((q[1] = (double)(gc_norm_vector[1] * a)));
+      p[0] = ((q[0] = gc_norm_vector[0] * a));
+      p[1] = ((q[1] = gc_norm_vector[1] * a));
       p[2] = z, q[2] = z;
       break;
     }
@@ -338,18 +167,18 @@ static int gcxlatc(
     default:
     case (2): {
 
-      long double a = -(gc_norm_vector[2] * z) / max_sq_gc_z;
-      long double b = sqrt(max_sq_gc_z - sq_z)/max_sq_gc_z;
-      long double a_n0 = a * gc_norm_vector[0];
-      long double a_n1 = a * gc_norm_vector[1];
-      long double b_n0 = b * gc_norm_vector[0];
-      long double b_n1 = b * gc_norm_vector[1];
+      double a = -(gc_norm_vector[2] * z) / max_sq_gc_z;
+      double b = sqrt(max_sq_gc_z - sq_z)/max_sq_gc_z;
+      double a_n0 = a * gc_norm_vector[0];
+      double a_n1 = a * gc_norm_vector[1];
+      double b_n0 = b * gc_norm_vector[0];
+      double b_n1 = b * gc_norm_vector[1];
 
-      p[0] = (double)(a_n0 + b_n1);
-      p[1] = (double)(a_n1 - b_n0);
+      p[0] = a_n0 + b_n1;
+      p[1] = a_n1 - b_n0;
       p[2] = z;
-      q[0] = (double)(a_n0 - b_n1);
-      q[1] = (double)(a_n1 + b_n0);
+      q[0] = a_n0 - b_n1;
+      q[1] = a_n1 + b_n0;
       q[2] = z;
       break;
     }
@@ -362,146 +191,11 @@ static int gcxlatc(
 #pragma _CRI opt
 #endif
 
-#else
-static int gcxlatc(
-  mpf_t const gc_norm_vector[3], double const z,
-  double p[3], double q[3]) {
-
-  // if the great circle is a circle of longitude
-  if (mpf_cmp_d(gc_norm_vector[2], -yac_angle_low_tol) > 0 &&
-      mpf_cmp_d(gc_norm_vector[2], yac_angle_low_tol) < 0) {
-     return loncxlatc(gc_norm_vector, z, p, q);
-  }
-
-  mpf_t sq_n0, sq_n1;
-  mpf_init2(sq_n0, YAC_MPF_PRECISION);
-  mpf_init2(sq_n1, YAC_MPF_PRECISION);
-  mpf_mul(sq_n0, gc_norm_vector[0], gc_norm_vector[0]);
-  mpf_mul(sq_n1, gc_norm_vector[1], gc_norm_vector[1]);
-
-  // square of highest z-value of great circle
-  mpf_t max_sq_gc_z;
-  mpf_init2(max_sq_gc_z, YAC_MPF_PRECISION);
-  mpf_add(max_sq_gc_z, sq_n0, sq_n1);
-
-  // square of z of lat circle
-  mpf_t sq_z;
-  mpf_init2(sq_z, YAC_MPF_PRECISION);
-  mpf_set_d(sq_z, z);
-  mpf_mul(sq_z, sq_z, sq_z);
-
-  int num_intersections;
-  // if both circles are the equator -> identical circles
-  if (mpf_cmp_d(max_sq_gc_z, yac_sq_angle_tol) < 0 &&
-      mpf_cmp_d(sq_z, yac_sq_angle_tol) < 0) {
-    num_intersections = -1;
-  } else {
-    // determine number of intersections
-    mpf_t buf;
-    mpf_init2(buf, YAC_MPF_PRECISION);
-    mpf_sub(buf, max_sq_gc_z, sq_z);
-
-    num_intersections =
-      ((mpf_cmp_d(buf, -tol) > 0) - (mpf_cmp_d(buf, tol) < 0)) + 1;
-
-    switch (num_intersections) {
-
-      // if no intersection is possible
-      case (0): break;
-
-      // the great circles touches the circle of latitude in a single point
-      // ==> (b = 0)
-      case (1): {
-        mpf_t a;
-        mpf_init2(a, YAC_MPF_PRECISION);
-        mpf_set_d(a, z);
-        mpf_mul(a, a, gc_norm_vector[2]);
-        mpf_div(a, a, max_sq_gc_z);
-        mpf_neg(a, a);
-
-        mpf_mul(buf, gc_norm_vector[0], a);
-        p[0] = ((q[0] = mpf_get_d(buf)));
-
-        mpf_mul(buf, gc_norm_vector[1], a);
-        p[1] = ((q[1] = mpf_get_d(buf)));
-        p[2] = z, q[2] = z;
-
-        mpf_clear(a);
-        break;
-      }
-
-      default:
-      case (2): {
-        mpf_t a;
-        mpf_init2(a, YAC_MPF_PRECISION);
-        mpf_set_d(a, z);
-        mpf_mul(a, a, gc_norm_vector[2]);
-        mpf_div(a, a, max_sq_gc_z);
-        mpf_neg(a, a);
-
-        mpf_t b;
-        mpf_init2(b, YAC_MPF_PRECISION);
-        mpf_sub(b, max_sq_gc_z, sq_z);
-        mpf_sqrt(b, b);
-        mpf_div(b, b, max_sq_gc_z);
-
-        mpf_t a_n0, a_n1, b_n0, b_n1;
-
-        mpf_init2(a_n0, YAC_MPF_PRECISION);
-        mpf_mul(a_n0, a, gc_norm_vector[0]);
-
-        mpf_init2(a_n1, YAC_MPF_PRECISION);
-        mpf_mul(a_n1, a, gc_norm_vector[1]);
-
-        mpf_init2(b_n0, YAC_MPF_PRECISION);
-        mpf_mul(b_n0, b, gc_norm_vector[0]);
-
-        mpf_init2(b_n1, YAC_MPF_PRECISION);
-        mpf_mul(b_n1, b, gc_norm_vector[1]);
-
-        mpf_add(buf, a_n0, b_n1);
-        p[0] = mpf_get_d(buf);
-
-        mpf_sub(buf, a_n1, b_n0);
-        p[1] = mpf_get_d(buf);
-
-        mpf_sub(buf, a_n0, b_n1);
-        q[0] = mpf_get_d(buf);
-
-        mpf_add(buf, a_n1, b_n0);
-        q[1] = mpf_get_d(buf);
-
-        p[2] = z;
-        q[2] = z;
-
-        mpf_clear(a);
-        mpf_clear(b);
-        mpf_clear(a_n0);
-        mpf_clear(a_n1);
-        mpf_clear(b_n0);
-        mpf_clear(b_n1);
-        break;
-      }
-    }
-
-    mpf_clear(buf);
-  }
-
-  mpf_clear(sq_n0);
-  mpf_clear(sq_n1);
-  mpf_clear(max_sq_gc_z);
-  mpf_clear(sq_z);
-
-  return num_intersections;
-}
-#endif
-
-#ifndef USE_MPF
 static int loncxlonc(
-  long double const norm_vector_a[3], long double const norm_vector_b[3],
+  double const norm_vector_a[3], double const norm_vector_b[3],
   double p[3], double q[3]) {
 
-  long double sq_plane_diff =
+  double sq_plane_diff =
     MIN((norm_vector_a[0] - norm_vector_b[0]) *
         (norm_vector_a[0] - norm_vector_b[0]) +
         (norm_vector_a[1] - norm_vector_b[1]) *
@@ -520,59 +214,23 @@ static int loncxlonc(
 
   return 2;
 }
-#else
-static int loncxlonc(
-  mpf_t const norm_vector_a[3], mpf_t const norm_vector_b[3],
-  double p[3], double q[3]) {
-
-  mpf_t sq_plane_diff;
-  mpf_init2(sq_plane_diff, YAC_MPF_PRECISION);
-  mpf_sub(sq_plane_diff, norm_vector_a[0], norm_vector_b[0]);
-  mpf_mul(sq_plane_diff, sq_plane_diff, sq_plane_diff);
-
-  mpf_t buf;
-  mpf_init2(buf, YAC_MPF_PRECISION);
-  mpf_sub(buf, norm_vector_a[1], norm_vector_b[1]);
-  mpf_mul(buf, buf, buf);
-
-  mpf_add(sq_plane_diff, sq_plane_diff, buf);
-
-  int ret;
-
-  // if both circles are on the same circle of longitude
-  if (mpf_cmp_d(sq_plane_diff, yac_sq_angle_tol) < 0) {
-    ret = -1;
-  } else {
-    mpf_set_d(buf, 4.0);
-    mpf_sub(buf, buf, sq_plane_diff);
-    if (mpf_cmp_d(buf, yac_sq_angle_tol) < 0) {
-      ret = -1;
-    } else {
-      // the intersection points of the two circles of longitude are the poles
-      p[0] = 0, p[1] = 0; p[2] = 1;
-      q[0] = 0, q[1] = 0; q[2] = -1;
-      ret = 2;
-    }
-  }
-
-  mpf_clear(sq_plane_diff);
-  mpf_clear(buf);
-  return ret;
-}
-#endif
 
 /** \example test_pxgc.c
  * This example contains some tests for the pxgc routine
  */
 
-#ifndef USE_MPF
 static int pxgc(
-  double const vec[3], long double const norm_vector[3], double p[3], double q[3]) {
+  double const vec[3], double const norm_vector[3], double p[3], double q[3]) {
 
   // compute the angle between the point vector and the norm vector
-  long double dot = (long double)(vec[0]) * norm_vector[0] +
-                    (long double)(vec[1]) * norm_vector[1] +
-                    (long double)(vec[2]) * norm_vector[2];
+  // we use kahan summation to increase precision
+  double dot = vec[0] * norm_vector[0];
+  double err = fma(vec[0], norm_vector[0], -dot);
+  dot = fma(vec[1], norm_vector[1], dot);
+  err += fma(vec[1], norm_vector[1], -vec[1]*norm_vector[1]);
+  dot = fma(vec[2], norm_vector[2], dot);
+  err += fma(vec[2], norm_vector[2], -vec[2]*norm_vector[2]);
+  dot += err;
 
   // if the point is not on the plane
   if (fabsl(dot) > yac_angle_tol) return 0;
@@ -582,43 +240,6 @@ static int pxgc(
   q[2] = -((p[2] = vec[2]));
   return 1;
 }
-#else
-static int pxgc(
-  double const vec[3], mpf_t const norm_vector[3], double p[3], double q[3]) {
-
-  // compute the angle between the point vector and the norm vector
-  mpf_t dot;
-  mpf_init2(dot, YAC_MPF_PRECISION);
-  mpf_set_d(dot, vec[0]);
-  mpf_mul(dot, dot, norm_vector[0]);
-
-  mpf_t buf;
-  mpf_init2(buf, YAC_MPF_PRECISION);
-  mpf_set_d(buf, vec[1]);
-  mpf_mul(buf, buf, norm_vector[1]);
-  mpf_add(dot, dot, buf);
-  mpf_set_d(buf, vec[2]);
-  mpf_mul(buf, buf, norm_vector[2]);
-  mpf_add(dot, dot, buf);
-
-  int ret;
-
-  // if the point is not on the plane
-  if (mpf_cmp_d(dot, -yac_angle_tol) < 0 ||
-      mpf_cmp_d(dot, yac_angle_tol) > 0) {
-    ret = 0;
-  } else {
-    q[0] = -((p[0] = vec[0]));
-    q[1] = -((p[1] = vec[1]));
-    q[2] = -((p[2] = vec[2]));
-    ret = 1;
-  }
-
-  mpf_clear(dot);
-  mpf_clear(buf);
-  return ret;
-}
-#endif
 
 static int pxlatc(
   double const vec[3], double z, double p[3], double q[3]) {
@@ -875,66 +496,20 @@ static int yac_check_pq_gcxgc(
          (vector_is_between(c, d, q, sq_len_diff_cd) << 3);
 }
 
-#ifndef USE_MPF
-static void compute_norm_vector_ld(
-  double const a[3], double const b[3], long double norm_vector[3]) {
-
-  long double const a_[3] = {a[0], a[1], a[2]};
-  long double const b_[3] = {b[0], b[1], b[2]};
+static void compute_norm_vector(
+  double const a[3], double const b[3], double norm_vector[3]) {
 
-  norm_vector[0] = a_[1] * b_[2] - a_[2] * b_[1];
-  norm_vector[1] = a_[2] * b_[0] - a_[0] * b_[2];
-  norm_vector[2] = a_[0] * b_[1] - a_[1] * b_[0];
+  norm_vector[0] = a[1] * b[2] - a[2] * b[1];
+  norm_vector[1] = a[2] * b[0] - a[0] * b[2];
+  norm_vector[2] = a[0] * b[1] - a[1] * b[0];
 
-  long double scale = 1.0 / sqrtl(norm_vector[0] * norm_vector[0] +
-                                  norm_vector[1] * norm_vector[1] +
-                                  norm_vector[2] * norm_vector[2]);
+  double scale = 1.0 / sqrt(norm_vector[0] * norm_vector[0] +
+                            norm_vector[1] * norm_vector[1] +
+                            norm_vector[2] * norm_vector[2]);
   norm_vector[0] *= scale;
   norm_vector[1] *= scale;
   norm_vector[2] *= scale;
 }
-#else
-static void compute_norm_vector_ld(
-  double const a[3], double const b[3], mpf_t norm_vector[3]) {
-  mpf_t a_[3], b_[3];
-  yac_mpf_set_vector_d(a_, a);
-  yac_mpf_set_vector_d(b_, b);
-
-  mpf_mul(norm_vector[0], a_[1], b_[2]);
-  mpf_mul(norm_vector[1], a_[2], b_[0]);
-  mpf_mul(norm_vector[2], a_[0], b_[1]);
-
-  mpf_t buf;
-  mpf_init2(buf, YAC_MPF_PRECISION);
-
-  mpf_mul(buf, a_[2], b_[1]);
-  mpf_sub(norm_vector[0], norm_vector[0], buf);
-  mpf_mul(buf, a_[0], b_[2]);
-  mpf_sub(norm_vector[1], norm_vector[1], buf);
-  mpf_mul(buf, a_[1], b_[0]);
-  mpf_sub(norm_vector[2], norm_vector[2], buf);
-
-  mpf_t scale;
-  mpf_init2(scale, YAC_MPF_PRECISION);
-
-  mpf_mul(scale, norm_vector[0], norm_vector[0]);
-  mpf_mul(buf, norm_vector[1], norm_vector[1]);
-  mpf_add(scale, scale, buf);
-  mpf_mul(buf, norm_vector[2], norm_vector[2]);
-  mpf_add(scale, scale, buf);
-  mpf_sqrt(scale, scale);
-  mpf_ui_div(scale, 1, scale);
-
-  mpf_mul(norm_vector[0], norm_vector[0], scale);
-  mpf_mul(norm_vector[1], norm_vector[1], scale);
-  mpf_mul(norm_vector[2], norm_vector[2], scale);
-
-  yac_mpf_clear_vector(a_);
-  yac_mpf_clear_vector(b_);
-  mpf_clear(buf);
-  mpf_clear(scale);
-}
-#endif
 
 /** \example test_gcxgc.c
  * This contains examples on how to use \ref yac_gcxgc_vec
@@ -957,15 +532,14 @@ static void compute_norm_vector_ld(
  *          5th bit will be set if both great circles are identically
  * @remarks both edges need to have length of at least yac_angle_tol
  */
-#ifndef USE_MPF
 static int yac_gcxgc_vec(
   double const a[3], double const b[3], double const c[3], double const d[3],
   double p[3], double q[3]) {
 
-  long double norm_vector_ab[3], norm_vector_cd[3];
+  double norm_vector_ab[3], norm_vector_cd[3];
 
-  compute_norm_vector_ld(a, b, norm_vector_ab);
-  compute_norm_vector_ld(c, d, norm_vector_cd);
+  compute_norm_vector(a, b, norm_vector_ab);
+  compute_norm_vector(c, d, norm_vector_cd);
 
   switch (gcxgc(norm_vector_ab, norm_vector_cd, p, q)) {
     default:
@@ -975,36 +549,6 @@ static int yac_gcxgc_vec(
       return yac_identical_gcxgc_vec(a, b, c, d, p, q);
   };
 }
-#else
-static int yac_gcxgc_vec(
-  double const a[3], double const b[3], double const c[3], double const d[3],
-  double p[3], double q[3]) {
-
-  mpf_t norm_vector_ab[3], norm_vector_cd[3];
-  yac_mpf_init_vector(norm_vector_ab);
-  yac_mpf_init_vector(norm_vector_cd);
-
-  compute_norm_vector_ld(a, b, norm_vector_ab);
-  compute_norm_vector_ld(c, d, norm_vector_cd);
-
-  int ret;
-
-  switch (gcxgc(norm_vector_ab, norm_vector_cd, p, q)) {
-    default:
-    case(2):
-      ret = yac_check_pq_gcxgc(a, b, c, d, p, q);
-      break;
-    case(-1):
-      ret = yac_identical_gcxgc_vec(a, b, c, d, p, q);
-      break;
-  };
-
-  yac_mpf_clear_vector(norm_vector_ab);
-  yac_mpf_clear_vector(norm_vector_cd);
-
-  return ret;
-}
-#endif
 
 /** \example test_latcxlatc.c
  * This contains examples on \ref yac_latcxlatc_vec
@@ -1039,59 +583,18 @@ static int yac_latcxlatc_vec(
         (vector_is_between_lat(a, b, d) << 3));
 }
 
-#ifndef USE_MPF
-static void compute_lon_norm_vector_ld(
-  double const a[3], double const b[3], long double norm_vector[3]) {
-
-  long double const a_[3] = {a[0], a[1], a[2]};
-  long double const b_[3] = {b[0], b[1], b[2]};
+static void compute_lon_norm_vector(
+  double const a[3], double const b[3], double norm_vector[3]) {
 
-  norm_vector[0] = a_[1] * b_[2] - a_[2] * b_[1];
-  norm_vector[1] = a_[2] * b_[0] - a_[0] * b_[2];
+  norm_vector[0] = a[1] * b[2] - a[2] * b[1];
+  norm_vector[1] = a[2] * b[0] - a[0] * b[2];
   norm_vector[2] = 0.0;
 
-  long double scale = 1.0 / sqrtl(norm_vector[0] * norm_vector[0] +
-                                  norm_vector[1] * norm_vector[1]);
+  double scale = 1.0 / sqrt(norm_vector[0] * norm_vector[0] +
+                            norm_vector[1] * norm_vector[1]);
   norm_vector[0] *= scale;
   norm_vector[1] *= scale;
 }
-#else
-static void compute_lon_norm_vector_ld(
-  double const a[3], double const b[3], mpf_t norm_vector[3]) {
-
-  mpf_t a_[3], b_[3];
-  yac_mpf_set_vector_d(a_, a);
-  yac_mpf_set_vector_d(b_, b);
-
-  mpf_mul(norm_vector[0], a_[1], b_[2]);
-  mpf_mul(norm_vector[1], a_[2], b_[0]);
-
-  mpf_t buf;
-  mpf_init2(buf, YAC_MPF_PRECISION);
-
-  mpf_mul(buf, a_[2], b_[1]);
-  mpf_sub(norm_vector[0], norm_vector[0], buf);
-  mpf_mul(buf, a_[0], b_[2]);
-  mpf_sub(norm_vector[1], norm_vector[1], buf);
-
-  mpf_t scale;
-  mpf_init2(scale, YAC_MPF_PRECISION);
-
-  mpf_mul(scale, norm_vector[0], norm_vector[0]);
-  mpf_mul(buf, norm_vector[1], norm_vector[1]);
-  mpf_add(scale, scale, buf);
-  mpf_sqrt(scale, scale);
-  mpf_ui_div(scale, 1, scale);
-
-  mpf_mul(norm_vector[0], norm_vector[0], scale);
-  mpf_mul(norm_vector[1], norm_vector[1], scale);
-
-  yac_mpf_clear_vector(a_);
-  yac_mpf_clear_vector(b_);
-  mpf_clear(buf);
-  mpf_clear(scale);
-}
-#endif
 
 /** \example test_loncxlonc.c
  * This contains examples on \ref yac_loncxlonc_vec
@@ -1110,14 +613,13 @@ static void compute_lon_norm_vector_ld(
  *      - 5th bit will be set if both edges are on the same circle of longitude
  * @remarks both edges need to have length of at least yac_angle_tol
  **/
-#ifndef USE_MPF
 static int yac_loncxlonc_vec(
   double const a[3], double const b[3], double const c[3], double const d[3],
   double p[3], double q[3]) {
 
-  long double norm_vector_ab[3], norm_vector_cd[3];
-  compute_lon_norm_vector_ld(a, b, norm_vector_ab);
-  compute_lon_norm_vector_ld(c, d, norm_vector_cd);
+  double norm_vector_ab[3], norm_vector_cd[3];
+  compute_lon_norm_vector(a, b, norm_vector_ab);
+  compute_lon_norm_vector(c, d, norm_vector_cd);
 
   switch(loncxlonc(norm_vector_ab, norm_vector_cd, p, q)) {
 
@@ -1128,36 +630,6 @@ static int yac_loncxlonc_vec(
       return yac_identical_gcxgc_vec(a, b, c, d, p, q);
   };
 }
-#else
-static int yac_loncxlonc_vec(
-  double const a[3], double const b[3], double const c[3], double const d[3],
-  double p[3], double q[3]) {
-
-  mpf_t norm_vector_ab[3], norm_vector_cd[3];
-  yac_mpf_init_vector(norm_vector_ab);
-  yac_mpf_init_vector(norm_vector_cd);
-
-  compute_lon_norm_vector_ld(a, b, norm_vector_ab);
-  compute_lon_norm_vector_ld(c, d, norm_vector_cd);
-
-  int ret;
-
-  switch(loncxlonc(norm_vector_ab, norm_vector_cd, p, q)) {
-
-    default:
-    case(2):
-      ret = yac_check_pq_gcxgc(a, b, c, d, p, q);
-      break;
-    case(-1):
-      ret = yac_identical_gcxgc_vec(a, b, c, d, p, q);
-      break;
-  };
-
-  yac_mpf_clear_vector(norm_vector_ab);
-  yac_mpf_clear_vector(norm_vector_cd);
-  return ret;
-}
-#endif
 
 // determines the return value for the provided intersection points p and q
 static int yac_check_pq_gcxlatc(
@@ -1191,35 +663,18 @@ static int yac_check_pq_gcxlatc(
  *      - 4th bit will be set if q is between c and d
  * @remarks both edges need to have length of at least yac_angle_tol
  **/
-#ifndef USE_MPF
 static int yac_loncxlatc_vec (
   double const a[3], double const b[3], double const c[3], double const d[3],
   double p[3], double q[3]) {
 
-  long double norm_vector_ab[3];
-  compute_lon_norm_vector_ld(a, b, norm_vector_ab);
+  double norm_vector_ab[3];
+  compute_lon_norm_vector(a, b, norm_vector_ab);
 
   int num_intersections = loncxlatc(norm_vector_ab, c[2], p, q);
   YAC_ASSERT(num_intersections == 2, "ERROR(yac_loncxlatc_vec): internal error")
 
   return yac_check_pq_gcxlatc(a, b, c, d, p, q);
 }
-#else
-static int yac_loncxlatc_vec (
-  double const a[3], double const b[3], double const c[3], double const d[3],
-  double p[3], double q[3]) {
-
-  mpf_t norm_vector_ab[3];
-  yac_mpf_init_vector(norm_vector_ab);
-  compute_lon_norm_vector_ld(a, b, norm_vector_ab);
-
-  int num_intersections = loncxlatc(norm_vector_ab, c[2], p, q);
-  YAC_ASSERT(num_intersections == 2, "ERROR(yac_loncxlatc_vec): internal error")
-
-  yac_mpf_clear_vector(norm_vector_ab);
-  return yac_check_pq_gcxlatc(a, b, c, d, p, q);
-}
-#endif
 
 /** \example test_gcxlatc.c
  * This contains examples on gcxlatc_vec.
@@ -1243,7 +698,6 @@ static int yac_loncxlatc_vec (
  *          p and q will be identically, but only the p bits will be set
  * @remarks both edges need to have length of at least yac_angle_tol
  **/
-#ifndef USE_MPF
 
 #if defined __INTEL_COMPILER
 #pragma intel optimization_level 0
@@ -1255,8 +709,8 @@ static int yac_gcxlatc_vec(
   double const a[3], double const b[3], double const c[3], double const d[3],
   double p[3], double q[3]) {
 
-  long double norm_vector_ab[3];
-  compute_norm_vector_ld(a, b, norm_vector_ab);
+  double norm_vector_ab[3];
+  compute_norm_vector(a, b, norm_vector_ab);
 
   int num_intersections = gcxlatc(norm_vector_ab, c[2], p, q);
 
@@ -1276,53 +730,19 @@ static int yac_gcxlatc_vec(
 #pragma _CRI opt
 #endif
 
-#else
-static int yac_gcxlatc_vec(
-  double const a[3], double const b[3], double const c[3], double const d[3],
-  double p[3], double q[3]) {
-
-  mpf_t norm_vector_ab[3];
-  yac_mpf_init_vector(norm_vector_ab);
-
-  compute_norm_vector_ld(a, b, norm_vector_ab);
-
-  int num_intersections = gcxlatc(norm_vector_ab, c[2], p, q);
-
-  int ret;
-
-  switch (num_intersections) {
-    default:
-    case(2):
-    case(1):
-      ret = yac_check_pq_gcxlatc(a, b, c, d, p, q);
-      break;
-    case(0):
-      ret = -1;
-      break;
-    case(-1):
-      ret = yac_latcxlatc_vec(a, b, c, d, p, q);
-      break;
-  }
-
-  yac_mpf_clear_vector(norm_vector_ab);
-  return ret;
-}
-#endif
-
 static int yac_pxp_vec(
   double const a[3], double const b[3], double p[3], double q[3]) {
 
   return (pxp(a, b, p, q))?(p_on_a + p_on_b):-1;
 }
 
-#ifndef USE_MPF
 static int yac_pxgc_vec(
   double const point[3], double const a[3], double const b[3],
   double p[3], double q[3]) {
 
   // compute norm vector for the plane of ab
-  long double norm_ab[3];
-  compute_norm_vector_ld(a, b, norm_ab);
+  double norm_ab[3];
+  compute_norm_vector(a, b, norm_ab);
 
   // if the point is on the plane of ab
   if (pxgc(point, norm_ab, p, q)) {
@@ -1339,35 +759,6 @@ static int yac_pxgc_vec(
     return -1;
   }
 }
-#else
-static int yac_pxgc_vec(
-  double const point[3], double const a[3], double const b[3],
-  double p[3], double q[3]) {
-
-  // compute norm vector for the plane of ab
-  mpf_t norm_ab[3];
-  yac_mpf_init_vector(norm_ab);
-  compute_norm_vector_ld(a, b, norm_ab);
-
-  int ret = pxgc(point, norm_ab, p, q);
-  yac_mpf_clear_vector(norm_ab);
-
-  // if the point is on the plane of ab
-  if (ret) {
-
-    int ret_value = p_on_a;
-    double sq_len_diff_ab = sq_len_diff_vec(a, b);
-
-    if      (vector_is_between(a, b, p, sq_len_diff_ab)) ret_value |= p_on_b;
-    else if (vector_is_between(a, b, q, sq_len_diff_ab)) ret_value |= q_on_b;
-
-    return ret_value;
-
-  } else {
-    return -1;
-  }
-}
-#endif
 
 static int yac_pxlat_vec(
   double const point[3], double const a[3], double const b[3],
diff --git a/src/lib/yac/interval_tree.c b/src/lib/yac/interval_tree.c
index c42480b9aee3a1874e3add3f72b1863cf776d698..987a9c154026e3480d5125e46b1c11ff0f8df723 100644
--- a/src/lib/yac/interval_tree.c
+++ b/src/lib/yac/interval_tree.c
@@ -1,54 +1,12 @@
-/**
- * @file interval_tree.c
- *
- * @copyright Copyright  (C)  2014 Moritz Hanke <hanke@dkrz.de>
- *                                 Thomas Jahns <jahns@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Thomas Jahns <jahns@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Thomas Jahns <jahns@dkrz.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include <stdlib.h>
 
 #include "ensure_array_size.h"
 #include "interval_tree.h"
-#include "utils.h"
+#include "utils_core.h"
 
 static int
 iv_compar(struct interval_node *a, struct interval_node *b)
diff --git a/src/lib/yac/interval_tree.h b/src/lib/yac/interval_tree.h
index d2f4a5605f8e7d69d1a8d0c95069088acd5704fc..8092e590e8d75a23a1e194a852e652667eefed89 100644
--- a/src/lib/yac/interval_tree.h
+++ b/src/lib/yac/interval_tree.h
@@ -1,48 +1,6 @@
-/**
- * @file interval_tree.h
- *
- * @copyright Copyright  (C)  2014 Moritz Hanke <hanke@dkrz.de>
- *                                 Thomas Jahns <jahns@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Thomas Jahns <jahns@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Thomas Jahns <jahns@dkrz.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 /** \example test_interval_tree.c
  * This contains a test of the interval_tree.
diff --git a/src/lib/yac/location.h b/src/lib/yac/location.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4556a39dd5edcee68ec4ff4e5b1cd8045a0ed4f
--- /dev/null
+++ b/src/lib/yac/location.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef LOCATION_H
+#define LOCATION_H
+
+#define YAC_MAX_LOC_STR_LEN 10
+
+enum yac_location {
+
+   YAC_LOC_CELL =   0,
+   YAC_LOC_CORNER = 1,
+   YAC_LOC_EDGE =   2,
+   YAC_LOC_UNDEFINED = 3,
+};
+
+enum yac_location yac_str2loc(char const * location);
+char const * yac_loc2str(enum yac_location location);
+enum yac_location yac_get_location(int const location);
+
+#endif // LOCATION_H
+
diff --git a/src/lib/yac/sphere_part.c b/src/lib/yac/sphere_part.c
index e42770e14dd8c31cffb5bde6e92d6d37bff084b1..bad6c17ad08c33756f5a6bac37dd9a464ebd1c3c 100644
--- a/src/lib/yac/sphere_part.c
+++ b/src/lib/yac/sphere_part.c
@@ -1,48 +1,6 @@
-/**
- * @file sphere_part.c
- *
- * @copyright Copyright  (C)  2014 Moritz Hanke <hanke@dkrz.de>
- *                                 Thomas Jahns <jahns@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Thomas Jahns <jahns@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifdef HAVE_CONFIG_H
 // Get the definition of the 'restrict' keyword.
@@ -58,9 +16,9 @@
 
 #include "sphere_part.h"
 #include "geometry.h"
-#include "grid.h"
+#include "basic_grid.h"
 #include "interval_tree.h"
-#include "utils.h"
+#include "utils_core.h"
 #include "ensure_array_size.h"
 
 union I_list {
@@ -202,7 +160,7 @@ static inline void compute_gc_norm_vector(
      balance_point[2] = prev_gc_norm_vector[1];
    }
 
-   crossproduct_ld(
+   crossproduct_kahan(
       balance_point, prev_gc_norm_vector, gc_norm_vector);
 
   if ((fabs(gc_norm_vector[0]) > 1e-9) ||
@@ -401,9 +359,9 @@ static void partition_data (
 
             // project the base vector of the current bounding circle onto the
             // current partitioning great circle
-            crossproduct_ld(parent_node->gc_norm_vector,
+            crossproduct_kahan(parent_node->gc_norm_vector,
                             curr_bnd_circle.base_vector, GCp);
-            crossproduct_ld(GCp, parent_node->gc_norm_vector, bVp);
+            crossproduct_kahan(GCp, parent_node->gc_norm_vector, bVp);
             YAC_ASSERT(
               (fabs(bVp[0]) > 1e-9) || (fabs(bVp[1]) > 1e-9) ||
               (fabs(bVp[2]) > 1e-9),
@@ -626,7 +584,7 @@ static inline int compare_points_int32_coord(
 
 static struct point_id_xyz *
   get_unique_points(
-    size_t * num_points, coordinate_pointer coordinates_xyz,
+    size_t * num_points, yac_const_coordinate_pointer coordinates_xyz,
     yac_int const * ids, int const * mask) {
 
   struct point_id_xyz_int32 * points_int32 =
@@ -699,7 +657,8 @@ static struct point_id_xyz *
 }
 
 struct point_sphere_part_search * yac_point_sphere_part_search_new (
-  size_t num_points, coordinate_pointer coordinates_xyz, yac_int const * ids) {
+  size_t num_points, yac_const_coordinate_pointer coordinates_xyz,
+  yac_int const * ids) {
 
   if (num_points == 0) return NULL;
 
@@ -724,7 +683,7 @@ struct point_sphere_part_search * yac_point_sphere_part_search_new (
 }
 
 struct point_sphere_part_search * yac_point_sphere_part_search_mask_new (
-  size_t num_points, coordinate_pointer coordinates_xyz,
+  size_t num_points, yac_const_coordinate_pointer coordinates_xyz,
   yac_int const * ids, int const * mask) {
 
   if (num_points == 0) return NULL;
@@ -778,8 +737,8 @@ static void search_bnd_circle_I_node(
 
     // project the base vector of the current bounding circle onto the
     // current great circle
-    crossproduct_ld(node->gc_norm_vector, bnd_circle.base_vector, GCp);
-    crossproduct_ld(GCp, node->gc_norm_vector, bVp);
+    crossproduct_kahan(node->gc_norm_vector, bnd_circle.base_vector, GCp);
+    crossproduct_kahan(GCp, node->gc_norm_vector, bVp);
     YAC_ASSERT(
       (fabs(bVp[0]) > 1e-9) || (fabs(bVp[1]) > 1e-9) || (fabs(bVp[2]) > 1e-9),
       "ERROR(search_bnd_circle_I_node): "
@@ -1815,8 +1774,8 @@ static void search_point(struct sphere_part_node * node,
 
          // project the point onto the current great circle
          double GCp[3], bVp[3];
-         crossproduct_ld(node->gc_norm_vector, point, GCp);
-         crossproduct_ld(GCp, node->gc_norm_vector, bVp);
+         crossproduct_kahan(node->gc_norm_vector, point, GCp);
+         crossproduct_kahan(GCp, node->gc_norm_vector, bVp);
 
          struct interval search_interval;
          // if the projected point does not coincide with the norm vector of
@@ -1862,7 +1821,7 @@ static void search_point(struct sphere_part_node * node,
 }
 
 void yac_bnd_sphere_part_search_do_point_search(
-  struct bnd_sphere_part_search * search, coordinate_pointer coordinates_xyz,
+  struct bnd_sphere_part_search * search, yac_coordinate_pointer coordinates_xyz,
   size_t count, size_t ** cells, size_t * num_cells_per_coordinate) {
 
   struct sphere_part_node * base_node = &(search->base_node);
diff --git a/src/lib/yac/sphere_part.h b/src/lib/yac/sphere_part.h
index 9c320e7be6c20baab4eb89e44d2ee3739787eb8d..a4b1f8a4fb879122d54e2bb9adbc23751df876a5 100644
--- a/src/lib/yac/sphere_part.h
+++ b/src/lib/yac/sphere_part.h
@@ -1,47 +1,6 @@
-/**
- * @file sphere_part.h
- *
- * @copyright Copyright  (C)  2014 Moritz Hanke <hanke@dkrz.de>
- *                                 Thomas Jahns <jahns@dkrz.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Thomas Jahns <jahns@dkrz.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #ifndef SPHERE_PART_H
 #define SPHERE_PART_H
@@ -105,7 +64,7 @@
  * - returns list of matching polygons
  */
 
-#include "grid.h"
+#include "basic_grid.h"
 #include "geometry.h"
 
 /** \example test_bnd_sphere_part.c
@@ -115,10 +74,11 @@
 struct point_sphere_part_search;
 
 struct point_sphere_part_search * yac_point_sphere_part_search_new (
-  size_t num_points, coordinate_pointer coordinates_xyz, yac_int const * ids);
+  size_t num_points, yac_const_coordinate_pointer coordinates_xyz,
+  yac_int const * ids);
 
 struct point_sphere_part_search * yac_point_sphere_part_search_mask_new (
-  size_t num_points, coordinate_pointer coordinates_xyz,
+  size_t num_points, yac_const_coordinate_pointer coordinates_xyz,
   yac_int const * ids, int const * mask);
 
 void yac_delete_point_sphere_part_search(
@@ -165,7 +125,7 @@ struct bnd_sphere_part_search * yac_bnd_sphere_part_search_new(
   struct bounding_circle * circles, size_t num_circles);
 void yac_bnd_sphere_part_search_delete(struct bnd_sphere_part_search * search);
 void yac_bnd_sphere_part_search_do_point_search(
-  struct bnd_sphere_part_search * search, coordinate_pointer coordinates_xyz,
+  struct bnd_sphere_part_search * search, yac_coordinate_pointer coordinates_xyz,
   size_t count, size_t ** cells, size_t * num_cells_per_coordinate);
 void yac_bnd_sphere_part_search_do_bnd_circle_search(
   struct bnd_sphere_part_search * search, struct bounding_circle * bnd_circles,
diff --git a/src/lib/yac/utils.c b/src/lib/yac/utils.c
deleted file mode 100644
index 484073beb41a1ff6ad7fc6ad1dfe0e875ce6d603..0000000000000000000000000000000000000000
--- a/src/lib/yac/utils.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * @file utils.c
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "utils.h"
-
-void yac_internal_abort_message(
-  const char * text, const char * file, int line) {
-
-  fprintf(stderr, "%s \n", text); 
-  fprintf(stderr, "Aborting in file %s, line %i ...\n", file, line );
-  exit(EXIT_FAILURE);
-}
-
-void yac_abort_message ( char * text, char * file, int line ) {
-  yac_internal_abort_message ( text, file, line );
-}
-
-int yac_file_exists(const char * filename) {
-  struct stat buffer;
-  return !stat(filename,&buffer);
-}
-
-char const * yac_name_type_pair_get_name(
-  struct yac_name_type_pair const * pairs, size_t count, int type) {
-
-  char const * name = NULL;
-  for (size_t i = 0; (i < count) && (name == NULL); ++i)
-    if (pairs[i].type == type) name = pairs[i].name;
-  return name;
-}
-
-int yac_name_type_pair_get_type(
-  struct yac_name_type_pair const * pairs, size_t count, char const * name) {
-
-  int type = INT_MAX;
-  if (name != NULL)
-    for (size_t i = 0; (i < count) && (type == INT_MAX); ++i)
-      if (!strcmp(pairs[i].name, name)) type = pairs[i].type;
-  return type;
-}
diff --git a/src/lib/yac/utils_common.h b/src/lib/yac/utils_common.h
new file mode 100644
index 0000000000000000000000000000000000000000..94e9f2232e280d0b241418effed4f49c30e20d1f
--- /dev/null
+++ b/src/lib/yac/utils_common.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef UTILS_COMMON_H
+#define UTILS_COMMON_H
+
+#include <stdlib.h>
+#ifdef YAC_FOR_CDO
+#include <stdint.h> // uint64_t
+#include <limits.h> // SIZE_MAX
+#define UNUSED(x) (void)(x)
+#define die(msg) abort()
+#define xmalloc(size) malloc(size)
+#define xrealloc(ptr,size) realloc(ptr,size)
+#define xcalloc(nmemb,size) calloc(nmemb,size)
+#else
+#include "ppm/ppm_xfuncs.h"
+#include "ppm/core.h"
+#endif
+#include "yac_types.h"
+
+int yac_file_exists(const char * filename);
+
+/**
+ * remove duplicated entries from a list of integers
+ * @param[in,out] array array containing a sorted (ascending) list of integers
+ * @param[in,out] n     number of entries in array
+ */
+static inline void yac_remove_duplicates_int(int * array, size_t * n) {
+
+   size_t const N = *n;
+   size_t pos = 0;
+
+   if (N == 0) return;
+
+   int prev = array[0];
+
+   for (size_t i = 1; i < N; ++i) {
+
+      if (array[i] == prev) continue;
+
+      prev = array[i];
+      ++pos;
+
+      if (pos != i) array[pos] = array[i];
+   }
+
+   *n = pos + 1;
+}
+
+struct yac_name_type_pair {
+  const char * name;
+  int type;
+};
+
+char const * yac_name_type_pair_get_name(
+  struct yac_name_type_pair const * pairs, size_t count, int type);
+int yac_name_type_pair_get_type(
+  struct yac_name_type_pair const * pairs, size_t count, char const * name);
+
+void yac_qsort_index(
+  void * a, size_t count, size_t size,
+  int (*compare)(void const *, void const *), size_t * idx);
+
+/* =======================================================================
+   Macros
+   ======================================================================= */
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define YAC_ASSERT(exp, msg) \
+  {if(!((exp))) die(((msg)));}
+
+#define YAC_ASSERT_F(exp, format, ...) \
+  { \
+    if(!((exp))) { \
+      char msg_buffer[1024]; \
+      int ret = snprintf( \
+        msg_buffer, sizeof(msg_buffer), ((format)), __VA_ARGS__); \
+      if ((ret >= 0) && ((size_t)ret < sizeof(msg_buffer))) \
+        die(((msg_buffer))); \
+      else \
+        die("an error occured, but error message could not be generated"); \
+    } \
+  }
+
+#endif // UTILS_H
+
diff --git a/src/lib/yac/utils_core.c b/src/lib/yac/utils_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..0076c1478bd2bb3e0b71cf291d6173e1c655e99d
--- /dev/null
+++ b/src/lib/yac/utils_core.c
@@ -0,0 +1,34 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "utils_core.h"
+
+int yac_file_exists(const char * filename) {
+  struct stat buffer;
+  return !stat(filename,&buffer);
+}
+
+char const * yac_name_type_pair_get_name(
+  struct yac_name_type_pair const * pairs, size_t count, int type) {
+
+  char const * name = NULL;
+  for (size_t i = 0; (i < count) && (name == NULL); ++i)
+    if (pairs[i].type == type) name = pairs[i].name;
+  return name;
+}
+
+int yac_name_type_pair_get_type(
+  struct yac_name_type_pair const * pairs, size_t count, char const * name) {
+
+  int type = INT_MAX;
+  if (name != NULL)
+    for (size_t i = 0; (i < count) && (type == INT_MAX); ++i)
+      if (!strcmp(pairs[i].name, name)) type = pairs[i].type;
+  return type;
+}
diff --git a/src/lib/yac/utils.h b/src/lib/yac/utils_core.h
similarity index 51%
rename from src/lib/yac/utils.h
rename to src/lib/yac/utils_core.h
index 7feca8723b33a856bd0a282a808ab9f2bcd09632..b4879b120ff49fe38bebbda02f30b3847cfc893e 100644
--- a/src/lib/yac/utils.h
+++ b/src/lib/yac/utils_core.h
@@ -1,115 +1,16 @@
-/**
- * @file utils.h
- * @brief Utlity functions
- *
- * Small general utility functions:
- *  - pointer-id conversion
- *  - hash
- *  - sorting
- *
- * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
- *                                 Rene Redler <rene.redler@mpimet.mpg.de>
- *
- * @version 1.0
- * @author Moritz Hanke <hanke@dkrz.de>
- *         Rene Redler <rene.redler@mpimet.mpg.de>
- */
-/*
- * Keywords:
- * Maintainer: Moritz Hanke <hanke@dkrz.de>
- *             Rene Redler <rene.redler@mpimet.mpg.de>
- * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yac/
- *
- * This file is part of YAC.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are  permitted provided that the following conditions are
- * met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of the DKRZ GmbH nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-#ifndef UTILS_H
-#define UTILS_H
-
-#include <stdlib.h>
-#ifdef YAC_FOR_CDO
-#include <stdint.h> // uint64_t
-#include <limits.h> // SIZE_MAX
-#define UNUSED(x) (void)(x)
-#define xmalloc(size) malloc(size)
-#define xrealloc(ptr,size) realloc(ptr,size)
-#define xcalloc(nmemb,size) calloc(nmemb,size)
-#define XT_INT_MAX SIZE_MAX
-typedef size_t yac_int;
-#else
-#include "core/ppm_xfuncs.h"
-#include "core/core.h"
-#endif
-
-typedef double(*coordinate_pointer)[3];
-typedef double const (* const const_coordinate_pointer)[3] ;
-
-#define YAC_ASSERT(exp, msg) \
-  {if(!((exp))) yac_internal_abort_message(((msg)), __FILE__, __LINE__);}
-
-#define YAC_ASSERT_F(exp, format, ...) \
-  { \
-    if(!((exp))) { \
-      char msg_buffer[1024]; \
-      int ret = snprintf( \
-        msg_buffer, sizeof(msg_buffer), ((format)), __VA_ARGS__); \
-      if ((ret >= 0) && ((size_t)ret < sizeof(msg_buffer))) \
-        yac_internal_abort_message(((msg_buffer)), __FILE__, __LINE__); \
-      else \
-        yac_internal_abort_message( \
-          "an error occured, but error message could not be generated", \
-          __FILE__, __LINE__); \
-    } \
-  }
+#ifndef UTILS_CORE_H
+#define UTILS_CORE_H
 
-/**
- * prints a short error message and info from where it was called
- * followed by an exit.
- */
-void yac_internal_abort_message ( const char * text, const char * file, int line );
-
-int yac_file_exists(const char * filename);
+#include "utils_common.h"
 
 /** \example test_abort_c.c
  * This contains an example of how to use yac_abort_message.
  */
 
-#define YAC_ABORT(msg) \
-  {yac_abort_message(((msg)), __FILE__, __LINE__);}
-
-/**
- * prints a short error message and info from where it was called
- * followed by an exit.
- */
-void yac_abort_message ( char * text, char * file, int line );
-
 /** \example test_quicksort.c
  * This contains an example of how to use quicksort_index.
  */
@@ -136,9 +37,6 @@ void yac_quicksort_index_int_size_t_size_t (
   int * a, size_t n, size_t * b, size_t * c );
 void yac_quicksort_index_int_size_t_yac_int (
   int * a, size_t n, size_t * b, yac_int * c );
-void yac_qsort_index(
-  void * a, size_t count, size_t size,
-  int (*compare)(void const *, void const *), size_t * idx);
 
 /** \example test_mergesort.c
  *
@@ -148,33 +46,6 @@ void yac_qsort_index(
 void yac_mergesort(void* base, size_t num, size_t size,
                    int (*compar)(const void*,const void*));
 
-/**
- * remove duplicated entries from a list of integers
- * @param[in,out] array array containing a sorted (ascending) list of integers
- * @param[in,out] n     number of entries in array
- */
-static inline void yac_remove_duplicates_int(int * array, size_t * n) {
-
-   size_t const N = *n;
-   size_t pos = 0;
-
-   if (N == 0) return;
-
-   int prev = array[0];
-
-   for (size_t i = 1; i < N; ++i) {
-
-      if (array[i] == prev) continue;
-
-      prev = array[i];
-      ++pos;
-
-      if (pos != i) array[pos] = array[i];
-   }
-
-   *n = pos + 1;
-}
-
 /**
  * remove duplicated entries from a list of integers
  * @param[in,out] array array containing a sorted (ascending) list of integers
@@ -328,24 +199,6 @@ static inline void yac_remove_duplicates_size_t_3(
    *n = pos + 1;
 }
 
-struct yac_name_type_pair {
-  const char * name;
-  int type;
-};
-
-char const * yac_name_type_pair_get_name(
-  struct yac_name_type_pair const * pairs, size_t count, int type);
-int yac_name_type_pair_get_type(
-  struct yac_name_type_pair const * pairs, size_t count, char const * name);
-
-/* =======================================================================
-   Macros
-   ======================================================================= */
-
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
 #define ASSERT(c) \
 if (!(c)) {\
    fprintf(stderr, "### Assertion violation: %s in %s:%d\n",\
@@ -353,5 +206,10 @@ if (!(c)) {\
    abort ();\
 }
 
-#endif // UTILS_H
+#define COPY_DATA(data, count) \
+  (memcpy( \
+    xmalloc((size_t)(count) * sizeof(*(data))), \
+    (data), (size_t)(count) * sizeof(*(data))))
+
+#endif // UTILS_CORE_H
 
diff --git a/src/lib/yac/yac_interp_method_conserv.c b/src/lib/yac/yac_interp_method_conserv.c
new file mode 100644
index 0000000000000000000000000000000000000000..bdb0471f6d13940f54a0addc24d7fb431144590a
--- /dev/null
+++ b/src/lib/yac/yac_interp_method_conserv.c
@@ -0,0 +1,1320 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+#ifdef HAVE_CONFIG_H
+// Get the definition of the 'restrict' keyword.
+#include "config.h"
+#endif
+*/
+#include <string.h>
+#include <math.h> // abs()
+/*
+#include "interp_method_internal.h"
+#include "interp_method_conserv.h"
+*/
+#include "yac_interp_method_conserv.h"
+#include "clipping.h"
+#include "area.h"
+#include "float.h"
+
+#include "utils_common.h"
+#include "geometry.h"
+
+struct yac_interp_grid {
+  char * src_grid_name;
+  char * tgt_grid_name;
+  //struct yac_dist_grid_pair * grid_pair;
+  struct yac_interp_field tgt_field;
+  size_t num_src_fields;
+  struct yac_interp_field src_fields[];
+};
+
+#define AREA_TOL_FACTOR (1e-6)
+/*
+static size_t do_search_conserv_1st_order(struct interp_method * method,
+                                          struct yac_interp_grid * interp_grid,
+                                          size_t * tgt_points, size_t count,
+                                          struct yac_interp_weights * weights);
+static size_t do_search_conserv_2nd_order(struct interp_method * method,
+                                          struct yac_interp_grid * interp_grid,
+                                          size_t * tgt_points, size_t count,
+                                          struct yac_interp_weights * weights);
+static void delete_conserv(struct interp_method * method);
+
+static struct interp_method_vtable
+  interp_method_conserv_1st_order_vtable = {
+    .do_search = do_search_conserv_1st_order,
+    .delete = delete_conserv};
+
+static struct interp_method_vtable
+  interp_method_conserv_2nd_order_vtable = {
+    .do_search = do_search_conserv_2nd_order,
+    .delete = delete_conserv};
+
+struct interp_method_conserv {
+
+  struct interp_method_vtable * vtable;
+  int partial_coverage;
+  enum yac_interp_method_conserv_normalisation normalisation;
+  int enforced_conserv;
+};
+*/
+struct weight_vector_data {
+  double weight;
+  size_t local_id;
+  yac_int global_id;
+};
+
+struct weight_vector_data_3d {
+  double weight[3];
+  size_t local_id;
+  yac_int global_id;
+};
+
+struct weight_vector_3d {
+  struct weight_vector_data_3d * data;
+  size_t n;
+};
+
+struct supermesh_cell {
+  struct {
+    size_t local_id;
+    yac_int global_id;
+  } src, tgt;
+  double norm_area;
+  double area;
+  double barycenter[3];
+  struct weight_vector_3d * src_cell_gradient;
+};
+
+typedef size_t const (* const const_size_t_2_pointer)[2];
+typedef int const * const const_int_pointer;
+typedef size_t const * const const_size_t_pointer;
+typedef yac_int const * const const_yac_int_pointer;
+typedef enum yac_edge_type const * const const_yac_edge_type_pointer;
+typedef struct bounding_circle const * const const_bounding_circle_pointer;
+typedef struct remote_point_infos const * const const_remote_point_infos_pointer;
+
+struct yac_const_basic_grid_data {
+  yac_const_coordinate_pointer vertex_coordinates;
+  const_yac_int_pointer ids[3];
+  const_int_pointer num_vertices_per_cell;
+  const_size_t_pointer cell_to_vertex;
+  const_size_t_pointer cell_to_vertex_offsets;
+  const_size_t_pointer cell_to_edge;
+  const_size_t_pointer cell_to_edge_offsets;
+  const_size_t_2_pointer edge_to_vertex;
+  const_bounding_circle_pointer cell_bnd_circles;
+  const_yac_edge_type_pointer edge_type;
+  const_remote_point_infos_pointer cell_owners;
+  const_remote_point_infos_pointer vertex_owners;
+  const_remote_point_infos_pointer edge_owners;
+  const size_t count[3];
+};
+
+static int get_max_num_vertices_per_cell(
+  struct yac_const_basic_grid_data * basic_grid_data) {
+
+  int max_num_vertices_per_cell = 0;
+  for (size_t i = 0; i < basic_grid_data->count[YAC_LOC_CELL]; ++i)
+    if (basic_grid_data->num_vertices_per_cell[i] >
+        max_num_vertices_per_cell)
+      max_num_vertices_per_cell =
+        basic_grid_data->num_vertices_per_cell[i];
+  return max_num_vertices_per_cell;
+}
+
+struct yac_const_basic_grid_data *
+yac_interp_grid_get_basic_grid_data_src(struct yac_interp_grid * interp_grid)
+{
+  return NULL;
+}
+
+struct yac_const_basic_grid_data *
+yac_interp_grid_get_basic_grid_data_tgt(struct yac_interp_grid * interp_grid)
+{
+  return NULL;
+}
+
+static void get_cell_buffers(
+  struct yac_interp_grid * interp_grid, size_t max_num_src_per_tgt,
+  struct grid_cell * tgt_grid_cell, struct grid_cell ** src_grid_cells) {
+
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+  struct yac_const_basic_grid_data * tgt_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_tgt(interp_grid);
+
+  *src_grid_cells = xmalloc(max_num_src_per_tgt * sizeof(**src_grid_cells));
+  enum yac_edge_type * edge_type_buffer;
+  double (*coordinates_xyz_buffer)[3];
+
+  // prepare source grid cell buffer
+  {
+    int max_num_vertices_per_cell =
+      MAX(get_max_num_vertices_per_cell(src_basic_grid_data),
+          get_max_num_vertices_per_cell(tgt_basic_grid_data));
+
+    edge_type_buffer =
+      xmalloc((max_num_src_per_tgt + 1) * (size_t)max_num_vertices_per_cell *
+              sizeof(*edge_type_buffer));
+    coordinates_xyz_buffer =
+      xmalloc((max_num_src_per_tgt + 1) * (size_t)max_num_vertices_per_cell *
+              sizeof(*coordinates_xyz_buffer));
+
+    tgt_grid_cell->coordinates_xyz = coordinates_xyz_buffer;
+    tgt_grid_cell->edge_type = edge_type_buffer;
+    tgt_grid_cell->array_size = max_num_vertices_per_cell;
+    for (size_t i = 0; i < max_num_src_per_tgt; ++i) {
+      (*src_grid_cells)[i].coordinates_xyz =
+        coordinates_xyz_buffer + (i + 1) * max_num_vertices_per_cell;
+      (*src_grid_cells)[i].edge_type =
+        edge_type_buffer + (i + 1) * max_num_vertices_per_cell;
+      (*src_grid_cells)[i].array_size = max_num_vertices_per_cell;
+    }
+  }
+}
+
+static void get_cell_buffers_(
+  struct yac_interp_grid * interp_grid,
+  struct grid_cell * tgt_grid_cell, struct grid_cell * src_grid_cell) {
+
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+  struct yac_const_basic_grid_data * tgt_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_tgt(interp_grid);
+
+  int max_num_vertices_per_cell =
+    MAX(get_max_num_vertices_per_cell(src_basic_grid_data),
+        get_max_num_vertices_per_cell(tgt_basic_grid_data));
+
+  enum yac_edge_type * edge_type_buffer =
+    xmalloc(2 * (size_t)max_num_vertices_per_cell *
+            sizeof(*edge_type_buffer));
+  yac_coordinate_pointer coordinates_xyz_buffer =
+    xmalloc(2 * (size_t)max_num_vertices_per_cell *
+            sizeof(*coordinates_xyz_buffer));
+
+  tgt_grid_cell->coordinates_xyz = coordinates_xyz_buffer;
+  tgt_grid_cell->edge_type = edge_type_buffer;
+  tgt_grid_cell->array_size = max_num_vertices_per_cell;
+
+  src_grid_cell->coordinates_xyz =
+    coordinates_xyz_buffer + max_num_vertices_per_cell;
+  src_grid_cell->edge_type =
+    edge_type_buffer + max_num_vertices_per_cell;
+  src_grid_cell->array_size = max_num_vertices_per_cell;
+}
+/*
+static int compute_1st_order_weights(
+  struct yac_const_basic_grid_data * tgt_basic_grid_data, size_t tgt_cell,
+  struct yac_const_basic_grid_data * src_basic_grid_data, size_t src_count,
+  size_t * src_cells, struct grid_cell tgt_grid_cell_buffer,
+  struct grid_cell * src_grid_cell_buffer,
+  double * weights, size_t * num_weights, int partial_coverage,
+  enum yac_interp_method_conserv_normalisation normalisation,
+  int enforced_conserv) {
+
+  yac_const_basic_grid_data_get_grid_cell(
+    tgt_basic_grid_data, tgt_cell, &tgt_grid_cell_buffer);
+  for (size_t i = 0; i < src_count; ++i)
+    yac_const_basic_grid_data_get_grid_cell(
+      src_basic_grid_data, src_cells[i], src_grid_cell_buffer + i);
+
+  double * area = weights;
+  yac_compute_overlap_areas(
+    src_count, src_grid_cell_buffer, tgt_grid_cell_buffer, area);
+
+  size_t num_valid_weights = 0;
+  for (size_t i = 0; i < src_count; ++i) {
+
+    if (area[i] > 0.0) {
+      if (i != num_valid_weights) {
+        area[num_valid_weights] = area[i];
+        src_cells[num_valid_weights] = src_cells[i];
+      }
+      ++num_valid_weights;
+    }
+  }
+  *num_weights = num_valid_weights;
+  if (num_valid_weights == 0) return 0;
+
+  double tgt_cell_area = yac_huiliers_area(tgt_grid_cell_buffer);
+  double norm_factor;
+
+  YAC_ASSERT(
+    (normalisation == YAC_INTERP_CONSERV_DESTAREA) ||
+    (normalisation == YAC_INTERP_CONSERV_FRACAREA),
+    "ERROR(compute_weights_order_first_conserv_no_partial): "
+    "invalid normalisation option in conservative remapping")
+  switch(normalisation) {
+    case(YAC_INTERP_CONSERV_DESTAREA):
+      norm_factor = 1.0 / tgt_cell_area;
+      break;
+    default:
+    case(YAC_INTERP_CONSERV_FRACAREA): {
+      double fracarea = 0.0;
+      for (size_t i = 0; i < num_valid_weights; ++i) fracarea += area[i];
+      norm_factor = 1.0 / fracarea;
+      break;
+    }
+  };
+
+  if (partial_coverage) {
+    for (size_t i = 0; i < num_valid_weights; ++i) weights[i] *= norm_factor;
+    return 1;
+  } else {
+    double tgt_cell_area_diff = tgt_cell_area;
+    double area_tol = tgt_cell_area * AREA_TOL_FACTOR;
+    for (size_t i = 0; i < num_valid_weights; ++i) {
+      double curr_area = area[i];
+      tgt_cell_area_diff -= curr_area;
+      weights[i] = curr_area * norm_factor;
+    }
+    int successful = fabs(tgt_cell_area_diff) <= area_tol;
+    if (successful && enforced_conserv)
+      yac_correct_weights(num_valid_weights, weights);
+    return successful;
+  }
+}
+
+static size_t do_search_conserv_1st_order (struct interp_method * method,
+                                           struct yac_interp_grid * interp_grid,
+                                           size_t * tgt_points, size_t count,
+                                           struct yac_interp_weights * weights) {
+
+  struct interp_method_conserv * method_conserv =
+    (struct interp_method_conserv *)method;
+
+  YAC_ASSERT(
+    yac_interp_grid_get_num_src_fields(interp_grid) == 1,
+    "ERROR(do_search_conserv): invalid number of source fields")
+
+  YAC_ASSERT(
+    yac_interp_grid_get_src_field_location(interp_grid, 0) == YAC_LOC_CELL,
+    "ERROR(do_search_conserv): unsupported source field location type")
+
+  YAC_ASSERT(
+    yac_interp_grid_get_tgt_field_location(interp_grid) == YAC_LOC_CELL,
+    "ERROR(do_search_conserv): unsupported target field location type")
+
+
+  size_t * src_cells = NULL;
+  size_t * num_src_per_tgt = xmalloc(count * sizeof(*num_src_per_tgt));
+
+  // search matching cells
+  yac_interp_grid_do_cell_search_src(
+    interp_grid, tgt_points, count, &src_cells, num_src_per_tgt);
+
+  // we did a search on the interp_grid, therefore we have to re-get the basic
+  // grid data
+  struct yac_const_basic_grid_data * tgt_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_tgt(interp_grid);
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+
+  size_t total_num_weights = 0;
+  size_t max_num_src_per_tgt = 0;
+  for (size_t i = 0; i < count; ++i) {
+    size_t curr_num_src_per_tgt = num_src_per_tgt[i];
+    if (curr_num_src_per_tgt > max_num_src_per_tgt)
+      max_num_src_per_tgt = curr_num_src_per_tgt;
+    total_num_weights += num_src_per_tgt[i];
+  }
+
+  // to ensure that the interpolation always procduces the same result, we
+  // sort the source cells for each target point by their global ids
+  {
+    yac_int * temp_src_global_ids =
+      xmalloc(max_num_src_per_tgt * sizeof(*temp_src_global_ids));
+
+    for (size_t i = 0, offset = 0; i < count; ++i) {
+
+      size_t curr_num_src_per_tgt = num_src_per_tgt[i];
+      size_t * curr_src_cells = src_cells + offset;
+      offset += curr_num_src_per_tgt;
+
+      for (size_t j = 0; j < curr_num_src_per_tgt; ++j)
+        temp_src_global_ids[j] =
+          src_basic_grid_data->ids[YAC_LOC_CELL][curr_src_cells[j]];
+
+      yac_quicksort_index_yac_int_size_t(
+        temp_src_global_ids, curr_num_src_per_tgt, curr_src_cells);
+    }
+
+    free(temp_src_global_ids);
+  }
+
+  double * w = xmalloc(total_num_weights * sizeof(*w));
+  size_t result_count = 0;
+  size_t * failed_tgt = xmalloc(count * sizeof(*failed_tgt));
+  total_num_weights = 0;
+
+  int partial_coverage = method_conserv->partial_coverage;
+  enum yac_interp_method_conserv_normalisation normalisation =
+    method_conserv->normalisation;
+  int enforced_conserv = method_conserv->enforced_conserv;
+
+  struct grid_cell tgt_grid_cell;
+  struct grid_cell * src_grid_cells;
+  get_cell_buffers(
+    interp_grid, max_num_src_per_tgt, &tgt_grid_cell, &src_grid_cells);
+
+  // compute overlaps
+  for (size_t i = 0, offset = 0, result_offset = 0; i < count; ++i) {
+
+    size_t curr_src_count = num_src_per_tgt[i];
+    size_t curr_tgt_point = tgt_points[i];
+    size_t num_weights;
+
+    // if weight computation was successful
+    if (compute_1st_order_weights(
+          tgt_basic_grid_data, curr_tgt_point,
+          src_basic_grid_data, curr_src_count, src_cells + offset,
+          tgt_grid_cell, src_grid_cells, w + result_offset, &num_weights,
+          partial_coverage, normalisation, enforced_conserv)) {
+
+      if (offset != result_offset) {
+
+        memmove(
+          src_cells + result_offset, src_cells + offset,
+          num_weights * sizeof(*src_cells));
+      }
+      tgt_points[result_count] = curr_tgt_point;
+      num_src_per_tgt[result_count] = num_weights;
+      result_count++;
+      result_offset += num_weights;
+      total_num_weights += num_weights;
+    } else {
+      failed_tgt[i - result_count] = curr_tgt_point;
+    }
+
+    offset += curr_src_count;
+  }
+
+  free(tgt_grid_cell.edge_type);
+  free(tgt_grid_cell.coordinates_xyz);
+  free(src_grid_cells);
+
+  if (result_count != count)
+    memcpy(tgt_points + result_count, failed_tgt,
+           (count - result_count) * sizeof(*tgt_points));
+  free(failed_tgt);
+
+  struct remote_points tgts = {
+    .data =
+      yac_interp_grid_get_tgt_remote_points(
+        interp_grid, tgt_points, result_count),
+    .count = result_count};
+  struct remote_point * srcs =
+    yac_interp_grid_get_src_remote_points(
+      interp_grid, 0, src_cells, total_num_weights);
+
+  // store weights
+  yac_interp_weights_add_wsum(
+    weights, &tgts, num_src_per_tgt, srcs, w);
+
+  free(tgts.data);
+  free(srcs);
+  free(src_cells);
+  free(num_src_per_tgt);
+  free(w);
+
+  return result_count;
+}
+*/
+static int
+compare_supermesh_cell_src_local_ids(const void * a, const void * b) {
+
+  struct supermesh_cell * a_ = (struct supermesh_cell *)a;
+  struct supermesh_cell * b_ = (struct supermesh_cell *)b;
+
+  int ret = (a_->src.local_id > b_->src.local_id) -
+            (a_->src.local_id < b_->src.local_id);
+  if (ret) return ret;
+  return (a_->tgt.global_id > b_->tgt.global_id) -
+         (a_->tgt.global_id < b_->tgt.global_id);
+}
+
+static int
+compare_supermesh_cell_tgt_local_ids(const void * a, const void * b) {
+
+  struct supermesh_cell * a_ = (struct supermesh_cell *)a;
+  struct supermesh_cell * b_ = (struct supermesh_cell *)b;
+
+  int ret = (a_->tgt.local_id > b_->tgt.local_id) -
+            (a_->tgt.local_id < b_->tgt.local_id);
+  if (ret) return ret;
+  return (a_->src.global_id > b_->src.global_id) -
+         (a_->src.global_id < b_->src.global_id);
+}
+
+static inline void orthogonalise_weight_vector(
+  double * src_cell_centroid, struct weight_vector_3d * G_i,
+  struct weight_vector_data_3d * buffer) {
+
+  // This routine computes: (I_3 - C_i * C_i^-1) * G_i
+  //
+  // O(g_i) = g_i - C_i * (C_i^-1 * g_i)
+  // where: C_i is the centeroid of a source cell
+  //        g_i is the gradient of the source field in C_i
+  //        O(g_i) is the projection of g_i into the plane perpendicular to C_i
+  // g_i = G_i * f
+  // where: G_i is the weight matrix to compute g_i
+  //        f is the source field vector
+  // => O(g_i) = (I_3 - C_i * C_i^-1) * G_i * f
+  // where: I_3 is the identity matrix of size 3 x 3
+  //
+  // M = I_3 - C_i * C_i^-1
+
+  double M[3][3];
+  for (size_t k = 0; k < 3; ++k)
+    for (size_t l = 0; l < 3; ++l)
+      M[k][l] = - src_cell_centroid[k] * src_cell_centroid[l];
+  for (size_t k = 0; k < 3; ++k)
+    M[k][k] += 1.0;
+
+  struct weight_vector_data_3d * G_i_data = G_i->data;
+
+  size_t N = G_i->n;
+  for (size_t i = 0; i < N; ++i) {
+    buffer[i].local_id = G_i_data[i].local_id;
+    buffer[i].global_id = G_i_data[i].global_id;
+    for (size_t j = 0; j < 3; ++j) buffer[i].weight[j] = 0.0;
+  }
+
+  for (size_t n = 0; n < N; ++n)
+    for (size_t i = 0; i < 3; ++i)
+      for (size_t j = 0; j < 3; ++j)
+        buffer[n].weight[i] += G_i_data[n].weight[j] * M[i][j];
+
+  memcpy(G_i->data, buffer, N * sizeof(*buffer));
+}
+
+static int compare_weight_vector_data_weight(
+  void const * a, void const * b) {
+
+  struct weight_vector_data const * weight_a =
+    (struct weight_vector_data const *)a;
+  struct weight_vector_data const * weight_b =
+    (struct weight_vector_data const *)b;
+
+  int ret = weight_a->global_id - weight_b->global_id;
+  if (ret) return ret;
+  double abs_weight_a = fabs(weight_a->weight);
+  double abs_weight_b = fabs(weight_b->weight);
+  ret = (abs_weight_a > abs_weight_b) - (abs_weight_a < abs_weight_b);
+  if (ret) return ret;
+  return (weight_a->weight > weight_b->weight) -
+         (weight_a->weight < weight_b->weight);
+}
+
+static int compare_weight_vector_data(
+  void const * a, void const * b) {
+
+  struct weight_vector_data const * weight_a =
+    (struct weight_vector_data const *)a;
+  struct weight_vector_data const * weight_b =
+    (struct weight_vector_data const *)b;
+
+  return weight_a->global_id - weight_b->global_id;
+}
+
+static void compact_weight_vector_data(
+  struct weight_vector_data * weights, size_t * n) {
+
+  size_t n_ = *n;
+
+  if (n_ <= 1) return;
+
+  // sort weights by global_id then by weight
+  qsort(weights, n_, sizeof(*weights), compare_weight_vector_data_weight);
+
+  size_t new_n = 1;
+  struct weight_vector_data * prev_weight_data = weights;
+  struct weight_vector_data * curr_weight_data = weights + 1;
+  for (size_t i = 1; i < n_; ++i, ++curr_weight_data) {
+
+    // if both weights refer to the same source point (by global_id)
+    if (!compare_weight_vector_data(prev_weight_data, curr_weight_data)) {
+      prev_weight_data->weight += curr_weight_data->weight;
+    } else {
+      ++new_n;
+      ++prev_weight_data;
+      *prev_weight_data = *curr_weight_data;
+    }
+  }
+
+  n_ = new_n;
+  new_n = 0;
+
+  // check for zero-weights
+  for (size_t i = 0; i < n_; ++i) {
+
+    if (weights[i].weight == 0.0) continue;
+    if (i != new_n) weights[new_n] = weights[i];
+    ++new_n;
+  }
+
+  *n = new_n;
+}
+
+static size_t compute_2nd_order_tgt_cell_weights(
+  struct supermesh_cell * super_cell, struct weight_vector_data * weights) {
+
+  weights[0].weight       = super_cell->norm_area;
+  weights[0].global_id    = super_cell->src.global_id;
+  weights[0].local_id     = super_cell->src.local_id;
+
+  struct weight_vector_3d * src_cell_gradient = super_cell->src_cell_gradient;
+
+  size_t N = src_cell_gradient->n;
+  // in case we have no gradient for the current supermesh cell,
+  // we assume a constant field across the whole associated source cell
+  if (N > 0) {
+    struct weight_vector_data_3d * gradient_weights = src_cell_gradient->data - 1;
+    double * overlap_barycenter = super_cell->barycenter;
+
+    for (size_t n = 1; n <= N; ++n) {
+      weights[n].weight       =
+        (gradient_weights[n].weight[0] * overlap_barycenter[0] +
+         gradient_weights[n].weight[1] * overlap_barycenter[1] +
+         gradient_weights[n].weight[2] * overlap_barycenter[2]) *
+        super_cell->norm_area;
+      weights[n].global_id    = gradient_weights[n].global_id;
+      weights[n].local_id     = gradient_weights[n].local_id;
+    }
+  }
+
+  return 1 + N;
+}
+
+static void compute_cell_barycenter(
+  struct yac_const_basic_grid_data * grid_data, size_t cell_idx,
+  double barycenter[3]) {
+
+  size_t num_vertices = grid_data->num_vertices_per_cell[cell_idx];
+  size_t const * vertices =
+    grid_data->cell_to_vertex + grid_data->cell_to_vertex_offsets[cell_idx];
+
+  barycenter[0] = 0.0;
+  barycenter[1] = 0.0;
+  barycenter[2] = 0.0;
+
+  for (size_t i = 0; i < num_vertices; ++i) {
+    double const * curr_vertex_coordinate =
+      grid_data->vertex_coordinates[vertices[i]];
+    barycenter[0] += curr_vertex_coordinate[0];
+    barycenter[1] += curr_vertex_coordinate[1];
+    barycenter[2] += curr_vertex_coordinate[2];
+  }
+  normalise_vector(barycenter);
+}
+
+static void compute_super_cells(
+  struct yac_interp_grid * interp_grid, size_t * tgt_points, size_t count,
+  struct supermesh_cell ** super_cells_, size_t * num_super_cells,
+  int * interp_fail_flag, size_t ** src_cells, size_t * num_src_cells,
+  enum yac_interp_method_conserv_normalisation normalisation,
+  int partial_coverage) {
+
+  YAC_ASSERT(
+    (normalisation == YAC_INTERP_CONSERV_DESTAREA) ||
+    (normalisation == YAC_INTERP_CONSERV_FRACAREA),
+    "ERROR(compute_super_cells): "
+    "invalid normalisation option in conservative remapping")
+
+  size_t * num_src_per_tgt = xmalloc(count * sizeof(*num_src_per_tgt));
+
+  // search for all source cell overlapping with the the target cells
+  printf("yac_interp_grid_do_cell_search_src() missing\n");
+  /*
+  yac_interp_grid_do_cell_search_src(
+    interp_grid, tgt_points, count, src_cells, num_src_per_tgt);
+  */
+
+  // determine the number of unique matching source cells
+  size_t total_num_overlaps = 0;
+  for (size_t i = 0; i < count; ++i) total_num_overlaps += num_src_per_tgt[i];
+  yac_quicksort_index_size_t_int(*src_cells, total_num_overlaps, NULL);
+  *num_src_cells = total_num_overlaps;
+  yac_remove_duplicates_size_t(*src_cells, num_src_cells);
+
+  size_t * num_tgt_per_src =
+    xrealloc(num_src_per_tgt, *num_src_cells * sizeof(*num_tgt_per_src));
+
+  // for some required source cells we may have not all required supermesh cells
+  // in that case we have to find the respective target cells and compute the
+  // missing supermesh cells from them
+  size_t * tgt_cells = NULL;
+  printf("yac_interp_grid_do_cell_search_tgt() missing\n");
+  /*
+  yac_interp_grid_do_cell_search_tgt(
+    interp_grid, *src_cells, *num_src_cells, &tgt_cells, num_tgt_per_src);
+  */
+
+  total_num_overlaps = 0;
+  for (size_t i = 0; i < *num_src_cells; ++i)
+    total_num_overlaps += num_tgt_per_src[i];
+
+  struct supermesh_cell * super_cells =
+    xmalloc(total_num_overlaps * sizeof(*super_cells));
+
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+  struct yac_const_basic_grid_data * tgt_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_tgt(interp_grid);
+
+  for (size_t i = 0, j = 0; i < *num_src_cells; ++i) {
+
+    size_t curr_num_overlaps = num_tgt_per_src[i];
+    size_t curr_src_cell = (*src_cells)[i];
+    yac_int curr_src_global_id =
+      src_basic_grid_data->ids[YAC_LOC_CELL][curr_src_cell];
+
+    for (size_t k = 0; k < curr_num_overlaps; ++k, ++j) {
+
+      size_t curr_tgt_cell = tgt_cells[j];
+      struct supermesh_cell * curr_super_cell = super_cells + j;
+      curr_super_cell->src.local_id = curr_src_cell;
+      curr_super_cell->src.global_id = curr_src_global_id;
+      curr_super_cell->tgt.local_id = curr_tgt_cell;
+      curr_super_cell->tgt.global_id =
+        tgt_basic_grid_data->ids[YAC_LOC_CELL][curr_tgt_cell];
+    }
+  }
+  free(tgt_cells);
+  free(num_tgt_per_src);
+
+  // sort supermesh_cell first by local ids of the target cells and
+  // second by global cell id
+  qsort(super_cells, total_num_overlaps, sizeof(*super_cells),
+        compare_supermesh_cell_tgt_local_ids);
+
+  struct grid_cell tgt_grid_cell;
+  struct grid_cell src_grid_cell;
+  get_cell_buffers_(interp_grid, &tgt_grid_cell, &src_grid_cell);
+
+  src_basic_grid_data = yac_interp_grid_get_basic_grid_data_src(interp_grid);
+  tgt_basic_grid_data = yac_interp_grid_get_basic_grid_data_tgt(interp_grid);
+
+  // For all supermesh cells compute the area and normalised area.
+  // Additionally, remove all empty supermesh cells.
+  size_t new_num_super_cells = 0;
+  size_t tgt_idx = 0;
+  for (size_t i = 0, j = 0; i < total_num_overlaps;) {
+
+    // get information about the current target cell
+    size_t curr_tgt_cell = super_cells[i].tgt.local_id;
+    printf("yac_const_basic_grid_data_get_grid_cell() missing\n");
+    /*
+    yac_const_basic_grid_data_get_grid_cell(
+      tgt_basic_grid_data, curr_tgt_cell, &tgt_grid_cell);
+    */
+    double curr_tgt_cell_coverage = 0.0;
+
+    // for all supermesh cells overlapping with the current target cell
+    for (;(i < total_num_overlaps) &&
+           (super_cells[i].tgt.local_id == curr_tgt_cell); ++i)  {
+
+      // get the current source cell
+      printf("yac_const_basic_grid_data_get_grid_cell() missing\n");
+      /*
+      yac_const_basic_grid_data_get_grid_cell(
+        src_basic_grid_data, super_cells[i].src.local_id, &src_grid_cell);
+      */
+
+      // compute area of the current supermesh cell
+      double super_cell_area;
+      double barycenter[3];
+      yac_compute_overlap_info(
+        1, &src_grid_cell, tgt_grid_cell, &super_cell_area, &barycenter);
+
+      // if there is an overlap between the current source and target cell
+      if (super_cell_area > 0.0) {
+
+        super_cells[new_num_super_cells].src = super_cells[i].src;
+        super_cells[new_num_super_cells].tgt = super_cells[i].tgt;
+        super_cells[new_num_super_cells].area = super_cell_area;
+        memcpy(super_cells[new_num_super_cells].barycenter, barycenter,
+               3 * sizeof(double));
+        super_cells[new_num_super_cells].src_cell_gradient = NULL;
+        ++new_num_super_cells;
+
+        curr_tgt_cell_coverage += super_cell_area;
+      }
+    }
+
+    double curr_tgt_cell_area = yac_huiliers_area(tgt_grid_cell);
+
+    // if there was an overlap
+    if (new_num_super_cells != j) {
+
+      double norm_factor;
+      YAC_ASSERT(
+        (normalisation == YAC_INTERP_CONSERV_DESTAREA) ||
+        (normalisation == YAC_INTERP_CONSERV_FRACAREA),
+        "ERROR(compute_super_cells): invalid normalisation")
+      switch (normalisation) {
+        default:
+        case (YAC_INTERP_CONSERV_DESTAREA):
+          norm_factor = 1.0 / curr_tgt_cell_area;
+          break;
+        case (YAC_INTERP_CONSERV_FRACAREA):
+          norm_factor = 1.0 / curr_tgt_cell_coverage;
+          break;
+      }
+      // compute normalised area
+      for (; j < new_num_super_cells; ++j)
+        super_cells[j].norm_area = super_cells[j].area * norm_factor;
+    }
+
+
+    // for target cell that do not overlap with any source cell
+    while ((tgt_idx < count) && (tgt_points[tgt_idx] < curr_tgt_cell))
+      interp_fail_flag[tgt_idx++] = 1;
+
+    if ((tgt_idx < count) && (tgt_points[tgt_idx] == curr_tgt_cell)) {
+
+      double area_tol = curr_tgt_cell_area * AREA_TOL_FACTOR;
+
+      if (partial_coverage) {
+        interp_fail_flag[tgt_idx] = curr_tgt_cell_coverage < area_tol;
+      } else {
+        interp_fail_flag[tgt_idx] =
+          fabs(curr_tgt_cell_area - curr_tgt_cell_coverage) > area_tol;
+      }
+      ++tgt_idx;
+    }
+  }
+  // for all remaining target cell that do not overlap with any source cell
+  for (; tgt_idx < count; ++tgt_idx) interp_fail_flag[tgt_idx] = 1;
+  *num_super_cells = new_num_super_cells;
+  *super_cells_ =
+    xrealloc(super_cells, new_num_super_cells * sizeof(*super_cells));
+  free(tgt_grid_cell.coordinates_xyz);
+  free(tgt_grid_cell.edge_type);
+}
+
+static yac_coordinate_pointer compute_src_cell_centroids(
+  struct yac_interp_grid * interp_grid,
+  size_t * src_cells, int * skip_src_cell, size_t num_src_cells,
+  struct supermesh_cell * super_cells, size_t num_super_cells) {
+
+  yac_coordinate_pointer src_cell_centroids =
+    xmalloc(num_src_cells * sizeof(*src_cell_centroids));
+
+  // sort supermesh cells first by source local id and second by
+  // target global id
+  qsort(super_cells, num_super_cells, sizeof(*super_cells),
+        compare_supermesh_cell_src_local_ids);
+
+  struct grid_cell src_grid_cell, dummy;
+  get_cell_buffers_(interp_grid, &src_grid_cell, &dummy);
+
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+
+  // compute centroids of source cells
+  // C_i = N(S_(U_k) (A_k*C_k))
+  // where: N(C) = C*(C*C)^-0.5 // normalisation
+  //        U_k all supermesh cells overlaping with the respective source cell
+  //        A_k area of supermesh cell
+  //        C_k barycenter of supermesh cell (normalisation of the sum of all
+  //                                          vertices of the cell)
+  for (size_t i = 0, offset = 0; i < num_src_cells; ++i) {
+
+    if (skip_src_cell[i]) continue;
+
+    size_t curr_src_cell = src_cells[i];
+    printf("yac_const_basic_grid_data_get_grid_cell() missing\n");
+    /*
+    yac_const_basic_grid_data_get_grid_cell(
+      src_basic_grid_data, curr_src_cell, &src_grid_cell);
+    */
+    double src_cell_area = yac_huiliers_area(src_grid_cell);
+
+    struct supermesh_cell * curr_super_cells = super_cells + offset;
+    size_t curr_num_super_cells = offset;
+    while ((offset < num_super_cells) &&
+           (super_cells[offset].src.local_id == curr_src_cell)) ++offset;
+    curr_num_super_cells = offset - curr_num_super_cells;
+
+    double src_cell_area_diff = src_cell_area;
+    double src_cell_centroid[3] = {0.0, 0.0, 0.0};
+
+    for (size_t j = 0; j < curr_num_super_cells; ++j) {
+
+      double super_cell_area = curr_super_cells[j].area;
+      double * super_cell_barycenter = curr_super_cells[j].barycenter;
+      src_cell_centroid[0] += super_cell_area * super_cell_barycenter[0];
+      src_cell_centroid[1] += super_cell_area * super_cell_barycenter[1];
+      src_cell_centroid[2] += super_cell_area * super_cell_barycenter[2];
+      src_cell_area_diff -= super_cell_area;
+    }
+
+    normalise_vector(src_cell_centroid);
+    src_cell_centroids[i][0] = src_cell_centroid[0];
+    src_cell_centroids[i][1] = src_cell_centroid[1];
+    src_cell_centroids[i][2] = src_cell_centroid[2];
+
+  }
+  free(src_grid_cell.coordinates_xyz);
+  free(src_grid_cell.edge_type);
+
+  return src_cell_centroids;
+}
+
+static struct weight_vector_3d * compute_src_cell_gradients(
+  struct yac_interp_grid * interp_grid, size_t * src_cells,
+  yac_coordinate_pointer src_cell_centroids, int * skip_src_cell,
+  size_t num_src_cells, size_t * src_cell_neighbours) {
+
+  struct weight_vector_3d * src_cell_gradients =
+    xmalloc(num_src_cells * sizeof(*src_cell_gradients));
+
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+
+  size_t total_num_gradient_weights = 0;
+  size_t max_num_neigh_per_src = 0;
+  for (size_t i = 0; i < num_src_cells; ++i) {
+    src_cell_gradients[i].data = NULL;
+    src_cell_gradients[i].n = 0;
+    size_t curr_num_neigh =
+      src_basic_grid_data->num_vertices_per_cell[src_cells[i]];
+    total_num_gradient_weights += curr_num_neigh + 1;
+    if (max_num_neigh_per_src < curr_num_neigh)
+      max_num_neigh_per_src = curr_num_neigh;
+  }
+  struct weight_vector_data_3d * weight_vector_data_buffer =
+    (total_num_gradient_weights > 0)?
+      (xmalloc(total_num_gradient_weights *
+               sizeof(*weight_vector_data_buffer))):NULL;
+  for (size_t i = 0; i < total_num_gradient_weights; ++i)
+    for (size_t j = 0; j < 3; ++j)
+      weight_vector_data_buffer[i].weight[j] = 0.0;
+
+  struct weight_vector_data_3d * orth_buffer =
+    xmalloc((max_num_neigh_per_src + 1) * sizeof(*orth_buffer));
+
+  // compute gradient in the centroid for each source cell
+  // g_i = O_i(g'_i)
+  // where: O_i(g) = g - C_i*(C_i*g) // makes g orthogonal to C_i
+  // g'_i = (A_(C_k))^-1 * S_ijk(((f_j + f_k) * 0.5 - f_i) *
+  //                         (C_j x C_k) * |C_j x C_k|^-1 *
+  //                         asin(|C_j x C_k|))
+  // where: g'_i is the estimated centroid gradient
+  //        A_(C_k) is the area of the cell generated by connecting the
+  //                barycenters of all neighbour cells
+  //        S_ijk is the sum of all edges of the previously described cell in
+  //              counterclockwise order
+  //        f_i, f_j, and f_k are the mean values of the field over the area of
+  //                          the respective source cell area
+  // asin(|C_j x C_k|) / |C_j x C_k| ~ 1.0
+  // => g'_i = S_ijk(((f_j + f_k) * 0.5 - f_i) * (C_j x C_k)) / A_(C_k)
+  //
+  // g_i = G_i * f
+  // G_i = S_ijk(((I_j + I_k) * 0.5 - I_i) * (C_j x C_k)) / A_(C_k)
+  // where: G_i is the gradient weight matrix
+  //        I_x is a vector of the same size as f, which is all zero except at
+  //            position x, where it is one
+  //        f is the source field vector
+  for (size_t i = 0, offset = 0, weight_vector_data_buffer_offset = 0;
+       i < num_src_cells; ++i) {
+
+    size_t curr_num_neigh =
+      src_basic_grid_data->num_vertices_per_cell[src_cells[i]];
+    size_t * curr_neighs = src_cell_neighbours + offset;
+    offset += curr_num_neigh;
+    struct weight_vector_3d * G_i = src_cell_gradients + i;
+    G_i->data = weight_vector_data_buffer + weight_vector_data_buffer_offset;
+
+    if (skip_src_cell[i]) continue;
+
+    weight_vector_data_buffer_offset += curr_num_neigh + 1;
+    G_i->n = curr_num_neigh + 1;
+    G_i->data[0].local_id = src_cells[i];
+    G_i->data[0].global_id =
+      src_basic_grid_data->ids[YAC_LOC_CELL][src_cells[i]];
+    for (size_t j = 0; j < curr_num_neigh; ++j) {
+      size_t curr_neigh = curr_neighs[j];
+      // if the current edge has a neighbour
+      if (curr_neigh != SIZE_MAX) {
+        G_i->data[j+1].local_id = curr_neigh;
+        G_i->data[j+1].global_id =
+            src_basic_grid_data->ids[YAC_LOC_CELL][curr_neigh];
+      } else {
+        // if the current edge has no neighbour, use current cell instead
+        G_i->data[j+1].local_id = G_i->data[0].local_id;
+        G_i->data[j+1].global_id = G_i->data[0].global_id;
+      }
+    }
+
+    // area of the polygon that is formed by connecting the barycenters of
+    // the neighbouring cells
+    double A_C_k = 0.0;
+
+    struct grid_cell centroid_triangle = {
+      .coordinates_xyz = (double[3][3]){{0}},
+      .edge_type =
+        (enum yac_edge_type[]) {GREAT_CIRCLE_EDGE,GREAT_CIRCLE_EDGE,GREAT_CIRCLE_EDGE},
+      .num_corners = 3, .array_size = 0};
+    centroid_triangle.coordinates_xyz[0][0] = src_cell_centroids[i][0];
+    centroid_triangle.coordinates_xyz[0][1] = src_cell_centroids[i][1];
+    centroid_triangle.coordinates_xyz[0][2] = src_cell_centroids[i][2];
+
+    double edge_direction = 0.0;
+
+    // We split the cell that is comprised of the barycenters of the edge
+    // neigbours into triangles, which has the centroid of the current cell
+    // as one corner. The sum of the areas of these triangles is A_C_K.
+    for (size_t j = 0; j < curr_num_neigh; ++j) {
+
+      size_t neigh_idx[2] = {j + 1, (j+1)%curr_num_neigh+1};
+      size_t neigh_local_ids[2] =
+        {G_i->data[neigh_idx[0]].local_id,
+         G_i->data[neigh_idx[1]].local_id};
+
+      if (neigh_local_ids[0] == neigh_local_ids[1]) continue;
+
+      double neigh_cell_barycenters[2][3];
+      compute_cell_barycenter(
+        src_basic_grid_data, neigh_local_ids[0], neigh_cell_barycenters[0]);
+      compute_cell_barycenter(
+        src_basic_grid_data, neigh_local_ids[1], neigh_cell_barycenters[1]);
+
+      centroid_triangle.coordinates_xyz[1][0] = neigh_cell_barycenters[0][0];
+      centroid_triangle.coordinates_xyz[1][1] = neigh_cell_barycenters[0][1];
+      centroid_triangle.coordinates_xyz[1][2] = neigh_cell_barycenters[0][2];
+      centroid_triangle.coordinates_xyz[2][0] = neigh_cell_barycenters[1][0];
+      centroid_triangle.coordinates_xyz[2][1] = neigh_cell_barycenters[1][1];
+      centroid_triangle.coordinates_xyz[2][2] = neigh_cell_barycenters[1][2];
+
+      A_C_k += yac_huiliers_area(centroid_triangle);
+
+      // C_j x C_k
+      double C_j_x_C_k[3];
+      crossproduct_kahan(
+        neigh_cell_barycenters[0], neigh_cell_barycenters[1], C_j_x_C_k);
+
+      double curr_edge_direction =
+        C_j_x_C_k[0] * centroid_triangle.coordinates_xyz[0][0] +
+        C_j_x_C_k[1] * centroid_triangle.coordinates_xyz[0][1] +
+        C_j_x_C_k[2] * centroid_triangle.coordinates_xyz[0][2];
+
+      if (fabs(curr_edge_direction) > fabs(edge_direction))
+        edge_direction = curr_edge_direction;
+
+      // -I_i * (C_j x C_k)
+      G_i->data[0].weight[0] -= C_j_x_C_k[0];
+      G_i->data[0].weight[1] -= C_j_x_C_k[1];
+      G_i->data[0].weight[2] -= C_j_x_C_k[2];
+
+      for (size_t l = 0; l < 3; ++l) C_j_x_C_k[l] *= 0.5;
+
+      // 0.5 * I_j * (C_j x C_k)
+      G_i->data[neigh_idx[0]].weight[0] += C_j_x_C_k[0];
+      G_i->data[neigh_idx[0]].weight[1] += C_j_x_C_k[1];
+      G_i->data[neigh_idx[0]].weight[2] += C_j_x_C_k[2];
+
+      // 0.5 * I_k * (C_j x C_k)
+      G_i->data[neigh_idx[1]].weight[0] += C_j_x_C_k[0];
+      G_i->data[neigh_idx[1]].weight[1] += C_j_x_C_k[1];
+      G_i->data[neigh_idx[1]].weight[2] += C_j_x_C_k[2];
+    } // curr_num_neigh
+
+    // if the neighbours were ordered in the wrong direction
+    if (edge_direction > 0.0)
+      for (size_t j = 0; j <= curr_num_neigh; ++j)
+        for (size_t k = 0; k < 3; ++k)
+          G_i->data[j].weight[k] *= -1.0;
+
+    double inv_A_C_K = (A_C_k > YAC_AREA_TOL)?(1.0/A_C_k):0.0;
+    // ((I_j + I_k) * 0.5 - I_i) * (C_j x C_k) / A_(C_k)
+    for (size_t k = 0; k <= curr_num_neigh; ++k)
+      for (size_t l = 0; l < 3; ++l)
+        G_i->data[k].weight[l] *= inv_A_C_K;
+
+    orthogonalise_weight_vector(
+      src_cell_centroids[i], G_i, orth_buffer);
+  } // num_src_cells
+  free(orth_buffer);
+
+  return src_cell_gradients;
+}
+/*
+static size_t compute_2nd_order_weights(
+  struct yac_interp_grid * interp_grid, size_t * tgt_cells,
+  int * interp_fail_flag, size_t num_tgt_cells,
+  struct supermesh_cell * super_cells, size_t num_super_cells,
+  size_t ** src_per_tgt, double ** weights, size_t * num_src_per_tgt) {
+
+  size_t num_interpolated_tgt = 0;
+
+  // sort supermesh cells first by target local id and second by
+  // source global id
+  qsort(super_cells, num_super_cells, sizeof(*super_cells),
+        compare_supermesh_cell_tgt_local_ids);
+
+  size_t max_num_weights_per_tgt = 0;
+  size_t max_num_total_weights = 0;
+
+  // count maximum number of weights
+  for (size_t i = 0, j = 0; i < num_tgt_cells; ++i) {
+
+    if (interp_fail_flag[i]) continue;
+
+    size_t curr_tgt_cell = tgt_cells[i];
+    size_t curr_num_weights = 0;
+
+    // skip supermesh cells not overlapping with current target cell
+    while ((j < num_super_cells) &&
+           (super_cells[j].tgt.local_id < curr_tgt_cell)) ++j;
+
+    // for all supermesh cells overlapping with the current target cell
+    while ((j < num_super_cells) &&
+           (super_cells[j].tgt.local_id == curr_tgt_cell))
+      curr_num_weights += 1 + super_cells[j++].src_cell_gradient->n;
+
+    max_num_total_weights += curr_num_weights;
+    if (max_num_weights_per_tgt < curr_num_weights)
+      max_num_weights_per_tgt = curr_num_weights;
+    num_interpolated_tgt++;
+  }
+
+  struct weight_vector_data * weight_buffer =
+    xmalloc(max_num_weights_per_tgt * sizeof(*weight_buffer));
+  *weights = xmalloc(max_num_total_weights * sizeof(**weights));
+  *src_per_tgt = xmalloc(max_num_total_weights * sizeof(**src_per_tgt));
+
+  // sort all target points that can be interpolated to the beginning of the
+  // tgt_cells array
+  yac_quicksort_index_int_size_t(interp_fail_flag, num_tgt_cells, tgt_cells);
+  yac_quicksort_index_size_t_int(tgt_cells, num_interpolated_tgt, NULL);
+
+  // compute 2nd order weights
+  // f_j = SUM(f_k') / A_j
+  // f_k' = A_k*(f_i + g_i * C_k)
+  //
+  // f_k' = A_k * (I_i + C_k * G_i) * f
+  // where: I_i is a vector of the same length as f, that is all 0.0 except at
+  //        position i, where it is 1.0
+  //        f is the source field vector
+  // => f_k' / A_j = M_k * f
+  // where: M_k = A_k / A_j * (I_i + C_k * G_i)
+  // => f_j = SUM(M_k) * f
+  //    f_j = M_j * f
+  // where: M_j = SUM(M_k)
+  size_t w_idx = 0;
+  for (size_t i = 0, j = 0; i < num_interpolated_tgt; ++i) {
+
+    size_t curr_tgt_cell = tgt_cells[i];
+
+    // skip supermesh cells not overlapping with current target cell
+    while ((j < num_super_cells) &&
+           (super_cells[j].tgt.local_id != curr_tgt_cell)) ++j;
+
+    size_t weight_buffer_offset = 0;
+
+    // for all supermesh cells overlapping with the current target cell
+    while ((j < num_super_cells) && 
+           (super_cells[j].tgt.local_id == curr_tgt_cell)) {
+
+      size_t num_weights =
+        compute_2nd_order_tgt_cell_weights(
+          super_cells + j, weight_buffer + weight_buffer_offset);
+      weight_buffer_offset += num_weights;
+      ++j;
+    }
+
+    // compact weights and sort them by global_id
+    compact_weight_vector_data(weight_buffer, &weight_buffer_offset);
+
+    num_src_per_tgt[i] = weight_buffer_offset;
+
+    for (size_t k = 0; k < weight_buffer_offset; ++k, ++w_idx) {
+      (*weights)[w_idx] = weight_buffer[k].weight;
+      (*src_per_tgt)[w_idx] = weight_buffer[k].local_id;
+    }
+  }
+  free(weight_buffer);
+
+  return num_interpolated_tgt;
+}
+
+static size_t do_search_conserv_2nd_order (struct interp_method * method,
+                                           struct yac_interp_grid * interp_grid,
+                                           size_t * tgt_points, size_t count,
+                                           struct yac_interp_weights * weights) {
+
+  struct interp_method_conserv * method_conserv =
+    (struct interp_method_conserv *)method;
+
+  YAC_ASSERT(
+    yac_interp_grid_get_num_src_fields(interp_grid) == 1,
+    "ERROR(do_search_conserv): invalid number of source fields")
+
+  YAC_ASSERT(
+    yac_interp_grid_get_src_field_location(interp_grid, 0) == YAC_LOC_CELL,
+    "ERROR(do_search_conserv): unsupported source field location type")
+
+  YAC_ASSERT(
+    yac_interp_grid_get_tgt_field_location(interp_grid) == YAC_LOC_CELL,
+    "ERROR(do_search_conserv): unsupported target field location type")
+
+  // sort target points
+  yac_quicksort_index_size_t_int(tgt_points, count, NULL);
+
+  int * interp_fail_flag = xmalloc(count * sizeof(*interp_fail_flag));
+  struct supermesh_cell * super_cells = NULL;
+  size_t num_super_cells = 0;
+  size_t * src_cells = NULL;
+  size_t num_src_cells = 0;
+
+  // compute the overlaps between the target cells and the source grid
+  compute_super_cells(
+    interp_grid, tgt_points, count, &super_cells, &num_super_cells,
+    interp_fail_flag, &src_cells, &num_src_cells,
+    method_conserv->normalisation, method_conserv->partial_coverage);
+
+  size_t total_num_src_cell_neighbours = 0;
+  struct yac_const_basic_grid_data * src_basic_grid_data =
+    yac_interp_grid_get_basic_grid_data_src(interp_grid);
+  for (size_t i = 0; i < num_src_cells; ++i)
+    total_num_src_cell_neighbours +=
+      src_basic_grid_data->num_vertices_per_cell[src_cells[i]];
+  size_t * src_cell_neighbours =
+    xmalloc(total_num_src_cell_neighbours * sizeof(*src_cell_neighbours));
+
+  // get the neighbours for all source cells overlapping with a target cell
+  yac_interp_grid_get_src_cell_neighbours(
+    interp_grid, src_cells, num_src_cells, src_cell_neighbours);
+
+  const_int_pointer src_cell_mask =
+    yac_interp_grid_get_src_field_mask(interp_grid, 0);
+  int * skip_src_cell = xmalloc(num_src_cells * sizeof(*skip_src_cell));
+
+  // check whether any required source cell or any of its neighbours is masked
+  // out in the field mask (deactivate respective cells)
+  if (src_cell_mask != NULL) {
+    for (size_t i = 0, offset = 0; i < num_src_cells; ++i) {
+      size_t curr_src_cell = src_cells[i];
+      if ((skip_src_cell[i] = !src_cell_mask[curr_src_cell])) continue;
+      size_t * curr_neighbours = src_cell_neighbours + offset;
+      size_t curr_num_neigh =
+        src_basic_grid_data->num_vertices_per_cell[curr_src_cell];
+      offset += curr_num_neigh;
+      for (size_t j = 0; j < curr_num_neigh; ++j)
+        if ((curr_neighbours[j] != SIZE_MAX) &&
+            (!src_cell_mask[curr_neighbours[j]]))
+          curr_neighbours[j] = SIZE_MAX;
+    }
+  } else {
+    memset(skip_src_cell, 0, num_src_cells * sizeof(*skip_src_cell));
+  }
+
+  // compute the centroids of all required source cells
+  yac_coordinate_pointer src_cell_centroids =
+    compute_src_cell_centroids(interp_grid, src_cells, skip_src_cell,
+                               num_src_cells, super_cells, num_super_cells);
+
+  // compute the gradients weights for the source cells
+  struct weight_vector_3d * src_cell_gradients =
+    compute_src_cell_gradients(
+      interp_grid, src_cells, src_cell_centroids,
+      skip_src_cell, num_src_cells, src_cell_neighbours);
+  free(src_cell_neighbours);
+  free(src_cell_centroids);
+
+  // sort supermesh cells by local src id
+  qsort(super_cells, num_super_cells, sizeof(*super_cells),
+        compare_supermesh_cell_src_local_ids);
+  for (size_t i = 0, j = 0; i < num_src_cells; ++i) {
+    size_t curr_src_cell = src_cells[i];
+    struct weight_vector_3d * curr_src_cell_gradient = src_cell_gradients + i;
+    // there should not be any supercell whose source cell is not in the list of
+    // required source cells
+    YAC_ASSERT(
+      (j >= num_super_cells) ||
+      (super_cells[j].src.local_id >= curr_src_cell),
+      "ERROR(do_search_conserv_2nd_order): internal error");
+    while ((j < num_super_cells) &&
+           (super_cells[j].src.local_id == curr_src_cell)) {
+      super_cells[j++].src_cell_gradient = curr_src_cell_gradient;
+    }
+  }
+  free(skip_src_cell);
+  free(src_cells);
+
+  size_t * src_per_tgt = NULL;
+  double * w = NULL;
+  size_t * num_src_per_tgt = xmalloc(count * sizeof(*num_src_per_tgt));
+
+  // compute the weights for the target points
+  size_t num_interpolated_tgt =
+    compute_2nd_order_weights(
+      interp_grid, tgt_points, interp_fail_flag, count,
+      super_cells, num_super_cells, &src_per_tgt, &w, num_src_per_tgt);
+  if (num_src_cells > 0) free(src_cell_gradients->data);
+  free(src_cell_gradients);
+  free(super_cells);
+  free(interp_fail_flag);
+
+  size_t total_num_weights = 0;
+  for (size_t i = 0; i < num_interpolated_tgt; ++i)
+    total_num_weights += num_src_per_tgt[i];
+
+  struct remote_points tgts = {
+    .data =
+      yac_interp_grid_get_tgt_remote_points(
+        interp_grid, tgt_points, num_interpolated_tgt),
+    .count = num_interpolated_tgt};
+  struct remote_point * srcs =
+    yac_interp_grid_get_src_remote_points(
+      interp_grid, 0, src_per_tgt, total_num_weights);
+
+  // store weights
+  yac_interp_weights_add_wsum(
+    weights, &tgts, num_src_per_tgt, srcs, w);
+
+  free(tgts.data);
+  free(srcs);
+  free(w);
+  free(num_src_per_tgt);
+  free(src_per_tgt);
+
+  return num_interpolated_tgt;
+}
+*/
+/*
+struct interp_method * yac_interp_method_conserv_new(
+  int order, int enforced_conserv, int partial_coverage,
+  enum yac_interp_method_conserv_normalisation normalisation) {
+
+  struct interp_method_conserv * method = xmalloc(1 * sizeof(*method));
+
+  YAC_ASSERT(
+    (enforced_conserv != 1) || (order == 1),
+    "ERROR(yac_interp_method_conserv_new): interp_method_conserv only "
+    "supports enforced_conserv with first order conservative remapping")
+  YAC_ASSERT(
+    (order == 1) || (order == 2),
+    "ERROR(yac_interp_method_conserv_new): invalid order")
+
+  method->vtable =
+    (order == 1)?
+      &interp_method_conserv_1st_order_vtable:
+      &interp_method_conserv_2nd_order_vtable;
+  method->partial_coverage = partial_coverage;
+  method->normalisation = normalisation;
+  method->enforced_conserv = enforced_conserv;
+
+  return (struct interp_method*)method;
+}
+
+static void delete_conserv(struct interp_method * method) {
+  free(method);
+}
+*/
diff --git a/src/lib/yac/yac_interp_method_conserv.h b/src/lib/yac/yac_interp_method_conserv.h
new file mode 100644
index 0000000000000000000000000000000000000000..2db1a58384817e60d7b416a7903b22fb8f984929
--- /dev/null
+++ b/src/lib/yac/yac_interp_method_conserv.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef INTERP_METHOD_CONSERV_H
+#define INTERP_METHOD_CONSERV_H
+/*
+#include "interp_method.h"
+*/
+/** \example test_interp_method_conserv_parallel.c
+ * A test for the parallel conservative‚ interpolation method.
+ */
+
+/**
+ * normalisation options for conservative interpolation\n
+ * (see SCRIP user manual for a more detailed description of the options)
+ */
+enum yac_interp_method_conserv_normalisation {
+   YAC_INTERP_CONSERV_DESTAREA = 0, //!< interpolation values are normalised by the area of
+                                    //!< the respective target cell
+                                    //!< (this is the default option)
+   YAC_INTERP_CONSERV_FRACAREA = 1, //!< interpolation values will be normalised by the area
+                                    //!< of the respective target cell that is covered by
+                                    //!< non-masked target cells
+};
+
+#define YAC_INTERP_CONSERV_ORDER_DEFAULT (1)
+#define YAC_INTERP_CONSERV_ENFORCED_CONSERV_DEFAULT (0)
+#define YAC_INTERP_CONSERV_PARTIAL_COVERAGE_DEFAULT (0)
+#define YAC_INTERP_CONSERV_NORMALISATION_DEFAULT (0)
+
+/**
+ * constructor for a interpolation method of type interp_method_conserv
+ * @param[in] order            1st or 2nd order remapping
+ * @param[in] enforced_conserv enforce conservation by correcting truncation errors
+ * @param[in] partial_coverage if == 0, target cells that are not completely
+ *                             covered by non-masked source cells will not be
+ *                             interpolated (this is the default option)\n
+ *                             if != 0, for target cells that are only
+ *                             partially covered by non-mask source cells the
+ *                             interpolation value will be determined by the
+ *                             contributions of the respective source cells
+ * @param[in] normalisation    specifies how to do the normalisation
+ */
+/*
+struct interp_method * yac_interp_method_conserv_new(
+  int order, int enforced_conserv, int partial_coverage,
+  enum yac_interp_method_conserv_normalisation normalisation);
+*/
+#endif // INTERP_METHOD_CONSERV_H
diff --git a/src/lib/yac/yac_types.h b/src/lib/yac/yac_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..7dab884a025b1e6b77422a4cc533d14e70a8ca0e
--- /dev/null
+++ b/src/lib/yac/yac_types.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2024 The YAC Authors
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef YAC_TYPES_H
+#define YAC_TYPES_H
+
+#ifdef YAC_FOR_CDO
+typedef size_t yac_int;
+#define XT_INT_MAX SIZE_MAX
+#else
+#include <yaxt.h>
+// idxtype from yaxt as global id type in yac
+typedef Xt_int yac_int;
+#define yac_int_dt Xt_int_dt
+#endif
+
+// types for 3D coordinate arrays
+typedef double(*yac_coordinate_pointer)[3];
+typedef double const (* const yac_const_coordinate_pointer)[3] ;
+
+// type for storing an array for two indices
+typedef size_t (* yac_size_t_2_pointer)[2];
+
+#endif // YAC_TYPES_H
diff --git a/src/lib/yac/yac_version.h b/src/lib/yac/yac_version.h
index 15b3a4e9b98d00bc0161f340412c9fee3fe0b616..95501afd72f8d8e09634e70c10e924275fd1f3e2 100644
--- a/src/lib/yac/yac_version.h
+++ b/src/lib/yac/yac_version.h
@@ -1,6 +1,6 @@
 #ifndef YAC_VERSION_H
 #define YAC_VERSION_H
 
-#define YAC_VERSION "3.0.1"
+#define YAC_VERSION "3.1.0"
 
 #endif
diff --git a/src/mapping.cc b/src/mapping.cc
index a6eb68f0a78b61d7a3b7a841737e02257eee018c..32cb2fe31f3a622d6da904f274f161f5dbb3bd8e 100644
--- a/src/mapping.cc
+++ b/src/mapping.cc
@@ -50,7 +50,7 @@ mapvar(int vlistID, int varID, const KeyValues &kv, const std::string &key, Cmor
         cdiDefAttTxt(vlistID, varID, "variable_comment", (int) value.size(), value.c_str());
       else if (key == "p")
         {
-          if (!isspace(value[0])) cdiDefAttTxt(vlistID, varID, "positive", (int) value.size(), value.c_str());
+          if (!std::isspace(value[0])) cdiDefAttTxt(vlistID, varID, "positive", (int) value.size(), value.c_str());
         }
       else
         {
diff --git a/src/merge_axis.cc b/src/merge_axis.cc
index 2a9e71426b6d8873ba71638c271b76abbf033872..a120b7ede0253f9f38b7cc6575b5166ce6a3b8d5 100644
--- a/src/merge_axis.cc
+++ b/src/merge_axis.cc
@@ -245,12 +245,12 @@ MergeVarsOnAxis::read_cmor_charvar(const std::vector<int> &axissize, int streamI
       while (nrecs--)
         {
           int varIDrw, levelIDrw;
-          size_t nmiss;
+          size_t numMissVals;
           streamInqRecord(streamID, &varIDrw, &levelIDrw);
           for (int i = 0; i < this->inputNames.nvalues; ++i)
             if (varIDrw == this->inputKeys[i].varID)
               {
-                streamReadRecord(streamID, buffer_old.data(), &nmiss);
+                streamReadRecord(streamID, buffer_old.data(), &numMissVals);
                 int newIndex;
                 for (int j = 0; j < oldgridsize; ++j)
                   {
diff --git a/src/module_info.cc b/src/module_info.cc
index 7e536e93c9ebcadde4adc5eeb8035ede6c9b0996..0423c76d2c325f367cd89b65d1b8297e0e8c1f59 100644
--- a/src/module_info.cc
+++ b/src/module_info.cc
@@ -3,12 +3,13 @@
 #include "mpmo_color.h"
 #include "modules.h"
 #include "util_string.h"
+#include "factory.h"
 #include <algorithm>
 #include <iostream>
 #include <functional>
 #include <string>
 
-typedef std::function<bool(module_t &mod)> ModuleQuery;
+typedef std::function<bool(const CdoModule &mod)> ModuleQuery;
 
 bool
 ModListOptions::requested(const std::string &name)
@@ -34,11 +35,12 @@ ModListOptions::parse_request(const std::string &requestString)
       all = false;
       for (size_t i = 0, n = splitString.size(); i < n; ++i)
         {
-          auto it = find_module(splitString[i]);
-          if (it != get_modules().end())
+          auto it = Factory::find_module(splitString[i]);
+          if (it != Factory::get().end())
             {
+              auto &module = Factory::get_module(it);
               operInfoRequested = true;
-              std::cerr << splitString[i] << ": " << it->second.toString() << std::endl;
+              std::cerr << splitString[i] << ": " << module.toString() << std::endl;
             }
           else
             {
@@ -56,54 +58,42 @@ ModListOptions::parse_request(const std::string &requestString)
   return true;
 }
 
-static std::string
-get_operator_description(const std::string &p_current_op_name, const char **help)
+std::string
+get_operator_description(const std::string &p_current_op_name, const CdoHelp &p_help)
 {
   std::string description = "";
-  unsigned long cur_help_idx = 0;
-  std::string line;
-  unsigned long operator_section = 0;
+  if (p_help.empty()) return description;
 
   // search for operator section
-  size_t help_size = 0;
-  while (help[help_size]) help_size++;
-  if (!help_size) return description;
-  while (operator_section == 0 && cur_help_idx < help_size - 1)
-    {
-      line = help[++cur_help_idx];
-      if (line.find("OPERATORS") != std::string::npos) { operator_section = cur_help_idx; }
-    }
+
+  auto it = std::find_if(begin(p_help), end(p_help), [&](const auto &l) { return l.find("OPERATORS") != std::string::npos; });
   // if no operator section is found
-  if (operator_section == 0)
+  if (it == end(p_help))
     {
-      cur_help_idx = 0;
-      line = help[0];
-      std::string name_section = help[0];
-      bool help_contains_name = false;
-      // search for the operator name in the description
-      while (!line.empty())
+      std::string name_section = std::string(p_help[0]);
+      it = std::find_if(begin(p_help), end(p_help),
+                        [&](const auto &l) { return l.find("    " + p_current_op_name) != std::string::npos; });
+
+      if (it != end(p_help))
         {
-          line = help[++cur_help_idx];
-          if (line.find(p_current_op_name) != std::string::npos) { help_contains_name = true; }
-          name_section += line;
+          name_section += *it;
+          description = name_section.substr(name_section.find_first_of('-') + 2, name_section.size());
         }
-      // if the name was found save description for later use
-      if (help_contains_name) { description = name_section.substr(name_section.find_first_of('-') + 2, name_section.size()); }
     }
   else
     {
-      line = help[++operator_section];
-      // search the operator section for current operator line
-      while (line.find(p_current_op_name + " ") == std::string::npos && !line.empty() && operator_section < help_size - 1)
-        {
-          line = help[++operator_section];
-        }
-      // if operator line found save description for later use
-      if (!line.empty() && line.find("    " + p_current_op_name + " ") != std::string::npos)
+      it = std::find_if(++it, end(p_help),
+                        [&](const auto &l) { return l.find("    " + p_current_op_name + " ") != std::string::npos; });
+      if (it != p_help.end())
         {
-          auto op_name_start = line.find_first_not_of(" \t");
+          std::string line = std::string(*it);
+          auto pos = line.find("    " + p_current_op_name + " ");
+          if (pos != std::string::npos)
+            {
+              auto op_name_start = line.find_first_not_of(" \t");
 
-          description = line.substr(line.find_first_not_of(" \t", op_name_start + p_current_op_name.size()), line.size());
+              description = line.substr(line.find_first_not_of(" \t", op_name_start + p_current_op_name.size()), line.size());
+            }
         }
     }
 
@@ -120,17 +110,18 @@ get_spacing_for(int p_space, const std::string &str)
 }
 
 static std::string
-operatorGetShortInfoString(std::string &current_op_name, const module_t &p_module)
+operatorGetShortInfoString(std::string &current_op_name, const CdoModule &p_module)
 {
   std::string shortInfo = current_op_name;
-  if (get_aliases().find(current_op_name) != get_aliases().end())
+  int alias_index = p_module.is_alias(current_op_name);
+  if (-1 != alias_index)
     {
-      shortInfo += std::string(get_spacing_for(16, current_op_name) + "--> " + get_aliases()[current_op_name]);
+      shortInfo += std::string(get_spacing_for(16, current_op_name) + "--> " + p_module.aliases[alias_index].original);
     }
-  else if (p_module.help)
+  else if (!p_module.get_help(current_op_name).empty())
     {
       // add spaceing and saving output line to the output list
-      auto description = get_operator_description(current_op_name, p_module.help);
+      const auto description = get_operator_description(current_op_name, p_module.get_help(current_op_name));
       shortInfo += get_spacing_for(16, current_op_name) + description;
     }
   std::string in_out_info
@@ -140,14 +131,13 @@ operatorGetShortInfoString(std::string &current_op_name, const module_t &p_modul
 }
 
 void
-operator_print_list(std::function<bool(module_t &)> selectionCriteria)
+operator_print_list(std::function<bool(const CdoModule &)> selectionCriteria)
 {
   std::vector<std::string> output_list;
 
-  // for (size_t out_list_idx = 0; out_list_idx < list_length; out_list_idx++)
-  for (auto &current_op_name : get_sorted_operator_name_list())
+  for (auto &current_op_name : Factory::get_sorted_operator_name_list())
     {
-      module_t &current_module = get_modules()[get_module_name_to(current_op_name)];
+      const CdoModule &current_module = Factory::get_module(current_op_name);
       if (selectionCriteria(current_module)) { output_list.push_back(operatorGetShortInfoString(current_op_name, current_module)); }
     }
   // print generated output list
@@ -161,27 +151,140 @@ operator_print_list(ModListOptions &p_opt)
 
   if (p_opt.printAll == true)
     {
-      operator_print_list([](module_t &) { return true; });
+      operator_print_list([](const CdoModule &) { return true; });
     }
   else
     {
 
-      ModuleQuery defaultModuleQuery = [](module_t &) -> bool { return false; };
-      ModuleQuery runquestDefaultModuleQuery = [](module_t &) -> bool { return true; };
+      ModuleQuery defaultModuleQuery = [](const CdoModule &) -> bool { return false; };
+      ModuleQuery runquestDefaultModuleQuery = [](const CdoModule &) -> bool { return true; };
 
       // clang-format off
-      ModuleQuery hasObase  = p_opt.requested(s_obase)     ? [](module_t &mod) -> bool { return mod.get_stream_out_cnt() == -1;        } : defaultModuleQuery;
-      ModuleQuery hasNoOut  = p_opt.requested(s_noOutput)  ? [](module_t &mod) -> bool { return mod.get_stream_out_cnt() ==  0;        } : defaultModuleQuery;
-      ModuleQuery hasArb    = p_opt.requested(s_arbIn)     ? [](module_t &mod) -> bool { return mod.get_stream_in_cnt()  == -1;        } : defaultModuleQuery;
-      ModuleQuery filesOnly = p_opt.requested(s_filesOnly) ? [](module_t &mod) -> bool { return mod.get_pos_restriction() == FilesOnly; } : defaultModuleQuery;
-      ModuleQuery onlyFirst = p_opt.requested(s_onlyFirst) ? [](module_t &mod) -> bool { return mod.get_pos_restriction() == OnlyFirst; } : defaultModuleQuery;
+      ModuleQuery hasObase  = p_opt.requested(s_obase)     ? [](const CdoModule &mod) -> bool { return mod.get_stream_out_cnt() == -1;        } : defaultModuleQuery;
+      ModuleQuery hasNoOut  = p_opt.requested(s_noOutput)  ? [](const CdoModule &mod) -> bool { return mod.get_stream_out_cnt() ==  0;        } : defaultModuleQuery;
+      ModuleQuery hasArb    = p_opt.requested(s_arbIn)     ? [](const CdoModule &mod) -> bool { return mod.get_stream_in_cnt()  == -1;        } : defaultModuleQuery;
+      ModuleQuery filesOnly = p_opt.requested(s_filesOnly) ? [](const CdoModule &mod) -> bool { return mod.get_pos_restriction() == FilesOnly; } : defaultModuleQuery;
+      ModuleQuery onlyFirst = p_opt.requested(s_onlyFirst) ? [](const CdoModule &mod) -> bool { return mod.get_pos_restriction() == OnlyFirst; } : defaultModuleQuery;
       // clang-format on
 
-      operator_print_list(
-          [&](module_t &mod) { return (hasObase(mod) || hasArb(mod) || hasNoOut(mod) || filesOnly(mod) || onlyFirst(mod)); });
+      operator_print_list([&](const CdoModule &mod) {
+        return (hasObase(mod) || hasArb(mod) || hasNoOut(mod) || filesOnly(mod) || onlyFirst(mod));
+      });
     }
 
   reset_text_color(stderr);
 
   return;
 }
+
+std::vector<std::string>
+get_no_output_operator_list()
+{
+  std::vector<std::string> names;
+  auto &factory = Factory::get();
+  for (auto &factory_entry : factory)
+    {
+      auto &module = Factory::get_module(factory_entry.first);
+      if (module.mode == 1 && module.constraints.streamOutCnt == 0) { names.push_back(factory_entry.first); }
+    }
+  std::sort(names.begin(), names.end());
+
+  return names;
+}
+
+void
+operatorPrintAll(void)
+{
+  int number_of_chars = 0;
+  std::string tab = "   ";
+  int tab_width = tab.size();
+  // using a set because it sorts the operators alphabetically on its own
+  std::vector<std::string> sorted_operator_names = Factory::get_sorted_operator_name_list();
+
+  std::cout << tab;
+  for (const auto &operatorName : sorted_operator_names)
+    {
+      if (number_of_chars > 85)
+        {
+          number_of_chars = tab_width;
+          std::cerr << std::endl << tab;
+        }
+
+      std::cerr << " " << operatorName;
+      number_of_chars += 1 + operatorName.size();
+    }
+
+  std::cerr << std::endl;
+}
+
+void
+operator_print_list(bool print_no_output)
+{
+  std::vector<std::string> output_list = print_no_output ? get_no_output_operator_list() : Factory::get_sorted_operator_name_list();
+
+  auto list_length = output_list.size();
+
+  // help variables
+
+  for (size_t out_list_idx = 0; out_list_idx < list_length; out_list_idx++)
+    {
+      const std::string current_op_name = output_list[out_list_idx];
+      auto &current_module = Factory::get_module(current_op_name);
+      if (current_module.is_alias(current_op_name) != -1)
+        {
+          output_list[out_list_idx] += get_spacing_for(16, current_op_name) + "--> " + Factory::get_original(current_op_name);
+        }
+      else if (current_module.get_help(current_op_name).empty())
+        {
+          // add spaceing and saving output line to the output list
+          auto description = get_operator_description(current_op_name, current_module.get_help(current_op_name));
+          output_list[out_list_idx] += get_spacing_for(16, current_op_name) + description;
+        }
+      std::string in_out_info = " (" + std::to_string(current_module.constraints.streamOutCnt) + "|"
+                                + std::to_string(current_module.constraints.streamOutCnt) + ")";
+      output_list[out_list_idx] += get_spacing_for(90, output_list[out_list_idx]) + in_out_info;
+    }
+  // print generated output list
+  for (const std::string &str : output_list) { std::cout << str << std::endl; }
+}
+
+void
+cdo_print_help(const CdoHelp &p_help)
+{
+  if (p_help.empty())
+    fprintf(stderr, "No help available for this operator!\n");
+  else
+    {
+      for (size_t i = 0; i < p_help.size(); ++i)
+        {
+          auto doPrint = !(p_help[i][0] == '\0' && p_help[i + 1][0] == ' ');
+          if (doPrint)
+            {
+
+              //===========================================================================
+              // This is necessary as long as we use fprintf.
+              // Without the string we cant use c_str and as such fprintf throws a seqfault
+              // As (14.11.2023) CdoHelp is a vector<string_view> and string_view does not
+              // provide a .c_str()
+              std::string line = std::string(p_help[i]);
+              //===========================================================================
+
+              if (color_enabled())
+                {
+                  if (cdo_cmpstr(line, "NAME") || cdo_cmpstr(line, "SYNOPSIS") || cdo_cmpstr(line, "DESCRIPTION")
+                      || cdo_cmpstr(line, "OPERATORS") || cdo_cmpstr(line, "NAMELIST") || cdo_cmpstr(line, "PARAMETER")
+                      || cdo_cmpstr(line, "ENVIRONMENT") || cdo_cmpstr(line, "NOTE") || cdo_cmpstr(line, "EXAMPLES"))
+                    {
+                      set_text_color(stdout, BRIGHT);
+                      fprintf(stdout, "%s", line.c_str());
+                      reset_text_color(stdout);
+                      fprintf(stdout, "\n");
+                    }
+                  else
+                    fprintf(stdout, "%s\n", line.c_str());
+                }
+              else { fprintf(stdout, "%s\n", line.c_str()); }
+            }
+        }
+    }
+}
diff --git a/src/module_info.h b/src/module_info.h
index b960537481bc97227be83eb19cd99603c7af0518..792df2da214d402fd5f002bc97a887674506da40 100644
--- a/src/module_info.h
+++ b/src/module_info.h
@@ -3,6 +3,9 @@
 
 #include <string>
 #include <map>
+#include <vector>
+
+#include "operator_help.h"  // for CdoHelp
 
 const std::string s_obase = "obase";
 const std::string s_arbIn = "arbitrary";
@@ -22,5 +25,9 @@ struct ModListOptions
   bool parse_request(const std::string &requestString);
 };
 
+std::string get_operator_description(const std::string &p_current_op_name, const std::vector<std::string> &p_help);
 void operator_print_list(ModListOptions &p_modListOpt);
+
+void cdo_print_help(const char **help);
+void cdo_print_help(const CdoHelp &p_help);
 #endif
diff --git a/src/module_list.h b/src/module_list.h
index 42f7b8f2ff683c40f0e940aa1aa048b76d84307f..5a9ff4292c357e5da71dd89bf4913ec3dfc6e0d5 100644
--- a/src/module_list.h
+++ b/src/module_list.h
@@ -1,325 +1,324 @@
-
-#ifndef MODULE_LIST_H
-#define MODULE_LIST_H
-
-#include "cdi.h"
-#include "modules.h"
-#include "module_definitions.h"
-#include "operator_help.h"
-
+//#ifndef MODULE_LIST_H
+//#define MODULE_LIST_H
+//
+//#include "cdi.h"
+//#include "modules.h"
+//#include "module_definitions.h"
+//#include "operator_help.h"
+//
 // clang-format off
-
-#define NO_HELP {}
-
-//                                             FuncName          Function         Help                OperatorList              internal                    RestrictionType
-//                                             |                 |                |                   |                         |          cdi-numbertype          |
-//                                             |                 |                |                   |                         |          |         in   out      |
-//                                             |                 |                |                   |                         |          |          |    |       |
-static const module_t module_Adisit         = {"Adisit"         ,Adisit         , AdisitHelp        , AdisitOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Afterburner    = {"Afterburner"    ,Afterburner    , AfterburnerHelp   , AfterburnerOperators    , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction,   {Alias ("afterburner", "after")}};
-static const module_t module_Arith          = {"Arith"          ,Arith          , ArithHelp         , ArithOperators          , EXPOSED  , CDI_BOTH , 2  , 1     , NoRestriction };
-static const module_t module_Arithc         = {"Arithc"         ,Arithc         , ArithcHelp        , ArithcOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Arithdays      = {"Arithdays"      ,Arithdays      , ArithdaysHelp     , ArithdaysOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Arithlat       = {"Arithlat"       ,Arithlat       , ArithlatHelp      , ArithlatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Apply          = {"Apply"          ,nullptr        , ApplyHelp         , {"apply"}               , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Bitrounding    = {"Bitrounding"    ,Bitrounding    , BitroundingHelp   , BitroundingOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Cat            = {"Cat"            ,Cat            , CopyHelp          , CatOperators            , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_CDItest        = {"CDItest"        ,CDItest        , NO_HELP           , CDItestOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_CDIread        = {"CDIread"        ,CDIread        , NO_HELP           , CDIreadOperators        , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
-static const module_t module_CDIwrite       = {"CDIwrite"       ,CDIwrite       , NO_HELP           , CDIwriteOperators       , EXPOSED  , CDI_REAL , 0  , 1     , NoRestriction };
-static const module_t module_Change         = {"Change"         ,Change         , ChangeHelp        , ChangeOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("chvar", "chname")} };
-static const module_t module_Change_e5slm   = {"Change_e5slm"   ,Change_e5slm   , NO_HELP           , Change_e5slmOperators   , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Cloudlayer     = {"Cloudlayer"     ,Cloudlayer     , NO_HELP           , CloudlayerOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_CMOR           = {"CMOR"           ,CMOR           , CMORHelp          , CMOROperators           , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
-static const module_t module_CMOR_lite      = {"CMORlite"       ,CMOR_lite      , CMORliteHelp      , CMORliteOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_CMOR_table     = {"CMORtable"      ,CMOR_table     , NO_HELP           , CMORtableOperators      , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
-static const module_t module_Collgrid       = {"Collgrid"       ,Collgrid       , CollgridHelp      , CollgridOperators       , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_Command        = {"Command"        ,Command        , NO_HELP           , CommandOperators        , INTERNAL , CDI_REAL , 1  , 0     , NoRestriction };
-static const module_t module_Comp           = {"Comp"           ,Comp           , CompHelp          , CompOperators           , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Compc          = {"Compc"          ,Compc          , CompcHelp         , CompcOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Complextorect  = {"Complextorect"  ,Complextorect  , NO_HELP           , ComplextorectOperators  , EXPOSED  , CDI_COMP , 1  , 2     , OnlyFirst     };
-static const module_t module_Cond           = {"Cond"           ,Cond           , CondHelp          , CondOperators           , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Cond2          = {"Cond2"          ,Cond2          , Cond2Help         , Cond2Operators          , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Condc          = {"Condc"          ,Condc          , CondcHelp         , CondcOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Consecstat     = {"Consecstat"     ,Consecstat     , ConsecstatHelp    , ConsecstatOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Copy           = {"Copy"           ,Copy           , CopyHelp          , CopyOperators           , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_DCW_util       = {"DCW_util"       ,DCW_util       , NO_HELP           , DCW_utilOperators       , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
-static const module_t module_Dayarith       = {"Dayarith"       ,Dayarith       , DayarithHelp      , DayarithOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Deltat         = {"Deltat"         ,Deltat         , DeltatHelp        , DeltatOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Deltime        = {"Deltime"        ,Deltime        , NO_HELP           , DeltimeOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Depth          = {"Depth"          ,Depth          , NO_HELP           , DepthOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Derivepar      = {"Derivepar"      ,Derivepar      , DeriveparHelp     , DeriveparOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction ,  {Alias ("geopotheight", "gheight")}};
-static const module_t module_Detrend        = {"Detrend"        ,Detrend        , DetrendHelp       , DetrendOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Diff           = {"Diff"           ,Diff           , DiffHelp          , DiffOperators           , EXPOSED  , CDI_BOTH , 2  , 0     , NoRestriction,   {Alias ("diffv", "diffn")}};
-static const module_t module_Distgrid       = {"Distgrid"       ,Distgrid       , DistgridHelp      , DistgridOperators       , EXPOSED  , CDI_REAL , 1  , OBASE , NoRestriction };
-static const module_t module_Duplicate      = {"Duplicate"      ,Duplicate      , DuplicateHelp     , DuplicateOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Echam5ini      = {"Echam5ini"      ,Echam5ini      , NO_HELP           , Echam5iniOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Enlarge        = {"Enlarge"        ,Enlarge        , EnlargeHelp       , EnlargeOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Enlargegrid    = {"Enlargegrid"    ,Enlargegrid    , NO_HELP           , EnlargegridOperators    , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Ensstat        = {"Ensstat"        ,Ensstat        , EnsstatHelp       , EnsstatOperators        , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_Ensstat3       = {"Ensstat3"       ,Ensstat3       , Ensstat2Help      , Ensstat3Operators       , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_Ensval         = {"Ensval"         ,Ensval         , EnsvalHelp        , EnsvalOperators         , EXPOSED  , CDI_REAL , -1 , OBASE , NoRestriction };
-static const module_t module_Eofcoeff       = {"Eofcoeff"       ,Eofcoeff       , EofcoeffHelp      , EofcoeffOperators       , EXPOSED  , CDI_REAL , 2  , OBASE , NoRestriction };
-static const module_t module_Eofcoeff3d     = {"Eofcoeff3d"     ,Eofcoeff3d     , EofcoeffHelp      , Eofcoeff3dOperators     , EXPOSED  , CDI_REAL , 2  , OBASE , NoRestriction };
-static const module_t module_EOFs           = {"EOFs"           ,EOFs           , EOFsHelp          , EOFsOperators           , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
-static const module_t module_EOF3d          = {"EOF3d"          ,EOF3d          , EOFsHelp          , EOF3dOperators          , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
-static const module_t module_EstFreq        = {"EstFreq"        ,EstFreq        , NO_HELP           , EstFreqOperators        , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Exprf          = {"Exprf"          ,Exprf          , ExprHelp          , ExprfOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_FC             = {"FC"             ,FC             , NO_HELP           , FCOperators             , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Filedes        = {"Filedes"        ,Filedes        , FiledesHelp       , FiledesOperators        , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction ,  {Alias ("vardes", "codetab")}};
-static const module_t module_Fillmiss       = {"Fillmiss"       ,Fillmiss       , NO_HELP           , FillmissOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Filter         = {"Filter"         ,Filter         , FilterHelp        , FilterOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Fldrms         = {"Fldrms"         ,Fldrms         , NO_HELP           , FldrmsOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Fldstat        = {"Fldstat"        ,Fldstat        , FldstatHelp       , FldstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("globavg", "fldavg") }};
-static const module_t module_Fldcor         = {"Fldcor"         ,Fldstat2       , FldcorHelp        , FldcorOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Fldcovar       = {"Fldcovar"       ,Fldstat2       , FldcovarHelp      , FldcovarOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Fourier        = {"Fourier"        ,Fourier        , FourierHelp       , FourierOperators        , EXPOSED  , CDI_COMP , 1  , 1     , NoRestriction };
-static const module_t module_Gengrid        = {"Gengrid"        ,Gengrid        , NO_HELP           , GengridOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Getgridcell    = {"Getgridcell"    ,Getgridcell    , GetgridcellHelp   , GetgridcellOperators    , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction };
-static const module_t module_Gradsdes       = {"Gradsdes"       ,Gradsdes       , GradsdesHelp      , GradsdesOperators       , EXPOSED  , CDI_REAL , 1  , 0     , FilesOnly };
-static const module_t module_Gridboxstat    = {"Gridboxstat"    ,Gridboxstat    , GridboxstatHelp   , GridboxstatOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Gridcell       = {"Gridcell"       ,Gridcell       , GridcellHelp      , GridcellOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Gridsearch     = {"Gridsearch"     ,Gridsearch     , NO_HELP           , GridsearchOperators     , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
-static const module_t module_Harmonic       = {"Harmonic"       ,Harmonic       , NO_HELP           , HarmonicOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Healpix        = {"Healpix"        ,Healpix        , HealpixHelp       , HealpixOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Histogram      = {"Histogram"      ,Histogram      , HistogramHelp     , HistogramOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Importamsr     = {"Importamsr"     ,Importamsr     , ImportamsrHelp    , ImportamsrOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Importbinary   = {"Importbinary"   ,Importbinary   , ImportbinaryHelp  , ImportbinaryOperators   , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("import_grads", "import_binary")} };
-static const module_t module_Importcmsaf    = {"Importcmsaf"    ,Importcmsaf    , ImportcmsafHelp   , ImportcmsafOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Importobs      = {"Importobs"      ,Importobs      , NO_HELP           , ImportobsOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Importfv3grid  = {"Importfv3grid"  ,Importfv3grid  , NO_HELP           , Importfv3gridOperators  , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Info           = {"Info"           ,Info           , InfoHelp          , InfoOperators           , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction };
-static const module_t module_Input          = {"Input"          ,Input          , InputHelp         , InputOperators          , EXPOSED  , CDI_REAL , 0  , 1     , NoRestriction };
-static const module_t module_Intgrid        = {"Intgrid"        ,Intgrid        , NO_HELP           , IntgridOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction ,  {Alias ("intgrid", "intgridbil")}};
-static const module_t module_Intgridtraj    = {"Intgridtraj"    ,Intgridtraj    , NO_HELP           , IntgridtrajOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Intlevel       = {"Intlevel"       ,Intlevel       , IntlevelHelp      , IntlevelOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Intlevel3d     = {"Intlevel3d"     ,Intlevel3d     , Intlevel3dHelp    , Intlevel3dOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Inttime        = {"Inttime"        ,Inttime        , InttimeHelp       , InttimeOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Intntime       = {"Intntime"       ,Intntime       , InttimeHelp       , IntntimeOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Intyear        = {"Intyear"        ,Intyear        , IntyearHelp       , IntyearOperators        , EXPOSED  , CDI_REAL , 2  , OBASE , NoRestriction };
-static const module_t module_Invert         = {"Invert"         ,Invert         , InvertHelp        , InvertOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Invertlev      = {"Invertlev"      ,Invertlev      , InvertlevHelp     , InvertlevOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Selsurface     = {"Selsurface"     ,Selsurface     , SelsurfaceHelp    , SelsurfaceOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Lic            = {"Lic"            ,Lic            , NO_HELP           , LicOperators            , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Longnfo        = {"Longinfo"       ,Longinfo       , NO_HELP           , LonginfoOperators       , EXPOSED  , CDI_REAL , 1 ,  0     , NoRestriction };
-static const module_t module_MapReduce      = {"MapReduce"      ,MapReduce      , MapReduceHelp     , MapReduceOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Maskbox        = {"Maskbox"        ,Maskbox        , MaskboxHelp       , MaskboxOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Maskregion     = {"Maskregion"     ,Maskbox        , MaskregionHelp    , MaskregionOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Mastrfu        = {"Mastrfu"        ,Mastrfu        , MastrfuHelp       , MastrfuOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Math           = {"Math"           ,Math           , MathHelp          , MathOperators           , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction,   {Alias ("log", "ln")} };
-static const module_t module_Merge          = {"Merge"          ,Merge          , MergeHelp         , MergeOperators          , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_Mergetime      = {"Mergetime"      ,Mergetime      , MergeHelp         , MergetimeOperators      , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_Mergegrid      = {"Mergegrid"      ,Mergegrid      , MergegridHelp     , MergegridOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Merstat        = {"Merstat"        ,Merstat        , MerstatHelp       , MerstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Monarith       = {"Monarith"       ,Monarith       , MonarithHelp      , MonarithOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Mrotuv         = {"Mrotuv"         ,Mrotuv         , NO_HELP           , MrotuvOperators         , EXPOSED  , CDI_REAL , 1  , 2     , NoRestriction };
-static const module_t module_Mrotuvb        = {"Mrotuvb"        ,Mrotuvb        , MrotuvbHelp       , MrotuvbOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_NCL_wind       = {"NCL_wind"       ,NCL_wind       , NCL_windHelp      , NCL_windOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Ninfo          = {"Ninfo"          ,Ninfo          , NinfoHelp         , NinfoOperators          , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction,   {Alias ("nvar", "npar")} };
-static const module_t module_Nmldump        = {"Nmldump"        ,Nmldump        , NO_HELP           , NmldumpOperators        , INTERNAL , CDI_REAL , 0  , 0     , NoRestriction };
-static const module_t module_Output         = {"Output"         ,Output         , OutputHelp        , OutputOperators         , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction };
-static const module_t module_Outputtab      = {"Outputtab"      ,Output         , OutputtabHelp     , OutputtabOperators      , EXPOSED  , CDI_REAL , -1 , 0     , NoRestriction,   {Alias ("outputkey", "outputtab")} };
-static const module_t module_Outputgmt      = {"Outputgmt"      ,Outputgmt      , OutputgmtHelp     , OutputgmtOperators      , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction,   {Alias ("outputcenter", "gmtxyz"), Alias("outputbounds", "gmtcells")} };
-static const module_t module_Pack           = {"Pack"           ,Pack           , PackHelp          , PackOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Pardup         = {"Pardup"         ,Pardup         , NO_HELP           , PardupOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Pinfo          = {"Pinfo"          ,Pinfo          , NO_HELP           , PinfoOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Query          = {"Query"          ,Query          , NO_HELP           , QueryOperators          , EXPOSED  , CDI_REAL , 1  , 1     , FilesOnly };
-static const module_t module_Pressure       = {"Pressure"       ,Pressure       , NO_HELP           , PressureOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("deltap_fl","deltap"),Alias("fpressure", "pressure_fl"), Alias("hpressure", "pressure_hl")}};
-static const module_t module_Recttocomplex  = {"Recttocomplex"  ,Recttocomplex  , NO_HELP           , RecttocomplexOperators  , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Regres         = {"Regres"         ,Regres         , RegresHelp        , RegresOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remap          = {"Remap"          ,Remapgrid      , RemapHelp         , RemapOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapbil       = {"Remapbil"       ,Remapgrid      , RemapbilHelp      , RemapbilOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapbic       = {"Remapbic"       ,Remapgrid      , RemapbicHelp      , RemapbicOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapnn        = {"Remapnn"        ,Remapgrid      , RemapnnHelp       , RemapnnOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapdis       = {"Remapdis"       ,Remapgrid      , RemapdisHelp      , RemapdisOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapcon       = {"Remapcon"       ,Remapgrid      , RemapconHelp      , RemapconOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("remapycon", "remapcon")} };
-static const module_t module_Remapycon2     = {"Remapycon2"     ,Remapgrid      , NO_HELP           , Remapycon2Operators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapscon      = {"Remapscon"      ,Remapgrid      , NO_HELP           , RemapsconOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapscon2     = {"Remapscon2"     ,Remapgrid      , NO_HELP           , Remapscon2Operators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remaplaf       = {"Remaplaf"       ,Remapgrid      , RemaplafHelp      , RemaplafOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapavg       = {"Remapavg"       ,Remapgrid      , NO_HELP           , RemapavgOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Genbil         = {"Genbil"         ,Remapweights   , RemapbilHelp      , GenbilOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Genbic         = {"Genbic"         ,Remapweights   , RemapbicHelp      , GenbicOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Gennn          = {"Gennn"          ,Remapweights   , RemapnnHelp       , GennnOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Gendis         = {"Gendis"         ,Remapweights   , RemapdisHelp      , GendisOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Gencon         = {"Gencon"         ,Remapweights   , RemapconHelp      , GenconOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias("genycon", "gencon")} };
-static const module_t module_Genycon2       = {"Genycon2"       ,Remapweights   , NO_HELP           , Genycon2Operators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Genscon        = {"Genscon"        ,Remapweights   , NO_HELP           , GensconOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Genscon2       = {"Genscon2"       ,Remapweights   , NO_HELP           , Genscon2Operators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Genlaf         = {"Genlaf"         ,Remapweights   , RemaplafHelp      , GenlafOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapstat      = {"Remapstat"      ,Remapstat      , RemapstatHelp     , RemapstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Remapeta       = {"Remapeta"       ,Remapeta       , RemapetaHelp      , RemapetaOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Replace        = {"Replace"        ,Replace        , ReplaceHelp       , ReplaceOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Replacevalues  = {"Replacevalues"  ,Replacevalues  , ReplacevaluesHelp , ReplacevaluesOperators  , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Rhopot         = {"Rhopot"         ,Rhopot         , RhopotHelp        , RhopotOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Rotuv          = {"Rotuv"          ,Rotuv          , RotuvbHelp        , RotuvOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Runpctl        = {"Runpctl"        ,Runpctl        , RunpctlHelp       , RunpctlOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Runstat        = {"Runstat"        ,Runstat        , RunstatHelp       , RunstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Samplegridicon = {"Samplegridicon" ,Samplegridicon , NO_HELP           , SamplegridiconOperators , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
-static const module_t module_Seascount      = {"Seascount"      ,Seascount      , NO_HELP           , SeascountOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Seaspctl       = {"Seaspctl"       ,Seaspctl       , SeaspctlHelp      , SeaspctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Seasstat       = {"Seasstat"       ,Seasstat       , SeasstatHelp      , SeasstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Seasmonstat    = {"Seasmonstat"    ,Seasmonstat    , NO_HELP           , SeasmonstatOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Selbox         = {"Selbox"         ,Selbox         , SelboxHelp        , SelboxOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Selgridcell    = {"Selgridcell"    ,Selgridcell    , SelgridcellHelp   , SelgridcellOperators    , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Select         = {"Select"         ,Select         , SelectHelp        , SelectOperators         , EXPOSED  , CDI_BOTH , -1 , 1     , NoRestriction };
-static const module_t module_Selvar         = {"Selvar"         ,Selvar         , SelvarHelp        , SelvarOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction,   {Alias ("selvar", "selname"), Alias("delvar"  , "delname"),Alias("selgridname"     , "selgrid")} };
-static const module_t module_Selrec         = {"Selrec"         ,Selrec         , SelvarHelp        , SelrecOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Selregion      = {"Selregion"      ,Selregion      , SelregionHelp     , SelregionOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Seloperator    = {"Seloperator"    ,Seloperator    , NO_HELP           , SeloperatorOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Seltime        = {"Seltime"        ,Seltime        , SeltimeHelp       , SeltimeOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction,   {Alias ("selseas", "selseason"), Alias("selmon", "selmonth")} };
-static const module_t module_Selyearidx     = {"Selyearidx"     ,Selyearidx     , SelyearidxHelp    , SelyearidxOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Set            = {"Set"            ,Set            , SetHelp           , SetOperators            , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction ,  {Alias ("setvar", "setname")}};
-static const module_t module_Setattribute   = {"Setattribute"   ,Setattribute   , SetattributeHelp  , SetattributeOperators   , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Setbox         = {"Setbox"         ,Setbox         , SetboxHelp        , SetboxOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Setgrid        = {"Setgrid"        ,Setgrid        , SetgridHelp       , SetgridOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Setgridcell    = {"Setgridcell"    ,Setgridcell    , SetgridcellHelp   , SetgridcellOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Sethalo        = {"Sethalo"        ,Sethalo        , SethaloHelp       , SethaloOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Setmiss        = {"Setmiss"        ,Setmiss        , SetmissHelp       , SetmissOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Setmisstonn    = {"Setmisstonn"    ,Fillmiss       , SetmissHelp       , SetmisstonnOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Setcodetab     = {"Setcodetab"     ,Setpartab      , SetHelp           , SetcodetabOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction ,  {Alias ("setpartab", "setcodetab")}};
-static const module_t module_Setpartab      = {"Setpartab"      ,Setpartab      , SetpartabHelp     , SetpartabOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("setpartabv", "setpartabn")} };
-static const module_t module_Setrcaname     = {"Setrcaname"     ,Setrcaname     , NO_HELP           , SetrcanameOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Settime        = {"Settime"        ,Settime        , SettimeHelp       , SettimeOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Setzaxis       = {"Setzaxis"       ,Setzaxis       , SetzaxisHelp      , SetzaxisOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Shiftxy        = {"Shiftxy"        ,Shiftxy        , ShiftxyHelp       , ShiftxyOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Showinfo       = {"Showinfo"       ,Showinfo       , ShowinfoHelp      , ShowinfoOperators       , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction ,  {Alias ("showvar", "showname")} };
-static const module_t module_Showattribute  = {"Showattribute"  ,Showattribute  , ShowattributeHelp , ShowattributeOperators  , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
-static const module_t module_Sinfo          = {"Sinfo"          ,Sinfo          , SinfoHelp         , SinfoOperators          , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction,   {Alias ("infov", "infon"),Alias("sinfov", "sinfon")} };
-static const module_t module_XSinfo         = {"XSinfo"         ,Sinfo          , XSinfoHelp        , XSinfoOperators         , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction };
-static const module_t module_Smooth         = {"Smooth"         ,Smooth         , SmoothHelp        , SmoothOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Sort           = {"Sort"           ,Sort           , NO_HELP           , SortOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("sortvar", "sortname")} };
-static const module_t module_Sorttimestamp  = {"Sorttimestamp"  ,Sorttimestamp  , NO_HELP           , SorttimestampOperators  , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-static const module_t module_Specinfo       = {"Specinfo"       ,Specinfo       , NO_HELP           , SpecinfoOperators       , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
-static const module_t module_Spectral       = {"Spectral"       ,Spectral       , SpectralHelp      , SpectralOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Specconv       = {"Specconv"       ,Spectral       , SpecconvHelp      , SpecconvOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Spectrum       = {"Spectrum"       ,Spectrum       , NO_HELP           , SpectrumOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Split          = {"Split"          ,Split          , SplitHelp         , SplitOperators          , EXPOSED  , CDI_BOTH , 1  , OBASE , NoRestriction,   {Alias ("splitvar", "splitname")} };
-static const module_t module_Splitdate      = {"Splitdate"      ,Splitdate      , SplitdateHelp     , SplitdateOperators      , EXPOSED  , CDI_BOTH , 1  , OBASE , OnlyFirst };
-static const module_t module_Splitrec       = {"Splitrec"       ,Splitrec       , SplitHelp         , SplitrecOperators       , EXPOSED  , CDI_BOTH , 1  , OBASE , NoRestriction };
-static const module_t module_Splitsel       = {"Splitsel"       ,Splitsel       , SplitselHelp      , SplitselOperators       , EXPOSED  , CDI_BOTH , 1  , OBASE , OnlyFirst };
-static const module_t module_Splittime      = {"Splittime"      ,Splittime      , SplittimeHelp     , SplittimeOperators      , EXPOSED  , CDI_BOTH , 1  , OBASE , OnlyFirst };
-static const module_t module_Splityear      = {"Splityear"      ,Splityear      , SplittimeHelp     , SplityearOperators      , EXPOSED  , CDI_BOTH , 1  , OBASE , NoRestriction };
-static const module_t module_Tee            = {"Tee"            ,Tee            , TeeHelp           , TeeOperators            , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Testdata       = {"Testdata"       ,Testdata       , NO_HELP           , TestdataOperators       , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Tests          = {"Tests"          ,Tests          , NO_HELP           , TestsOperators          , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Timcount       = {"Timcount"       ,Timcount       , NO_HELP           , TimcountOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Yearcount      = {"Yearcount"      ,Timcount       , NO_HELP           , YearcountOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Moncount       = {"Moncount"       ,Timcount       , NO_HELP           , MoncountOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Daycount       = {"Daycount"       ,Timcount       , NO_HELP           , DaycountOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Hourcount      = {"Hourcount"      ,Timcount       , NO_HELP           , HourcountOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Timcumsum      = {"Timcumsum"      ,Timcumsum      , TimcumsumHelp     , TimcumsumOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Timpctl        = {"Timpctl"        ,Timpctl        , TimpctlHelp       , TimpctlOperators        , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Yearpctl       = {"Yearpctl"       ,Timpctl        , YearpctlHelp      , YearpctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Monpctl        = {"Monpctl"        ,Timpctl        , MonpctlHelp       , MonpctlOperators        , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Daypctl        = {"Daypctl"        ,Timpctl        , DaypctlHelp       , DaypctlOperators        , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Hourpctl       = {"Hourpctl"       ,Timpctl        , HourpctlHelp      , HourpctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Timselpctl     = {"Timselpctl"     ,Timselpctl     , TimselpctlHelp    , TimselpctlOperators     , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Timfillmiss    = {"Timfillmiss"    ,Timfillmiss    , TimfillmissHelp   , TimfillmissOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Timsort        = {"Timsort"        ,Timsort        , TimsortHelp       , TimsortOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("sort", "timsort")} };
-static const module_t module_Timselstat     = {"Timselstat"     ,Timselstat     , TimselstatHelp    , TimselstatOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_XTimstat       = {"XTimstat"       ,XTimstat       , NO_HELP           , XTimstatOperators       , INTERNAL , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Timstat        = {"Timstat"        ,Timstat        , TimstatHelp       , TimstatOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Yearstat       = {"Yearstat"       ,Timstat        , YearstatHelp      , YearstatOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Monstat        = {"Monstat"        ,Timstat        , MonstatHelp       , MonstatOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Daystat        = {"Daystat"        ,Timstat        , DaystatHelp       , DaystatOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Hourstat       = {"Hourstat"       ,Timstat        , HourstatHelp      , HourstatOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
-static const module_t module_Timcor         = {"Timcor"         ,Timstat2       , TimcorHelp        , TimcorOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Timcovar       = {"Timcovar"       ,Timstat2       , TimcovarHelp      , TimcovarOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Timrmsd        = {"Timrmsd"        ,Timstat2       , NO_HELP           , TimrmsdOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Timstat3       = {"Timstat3"       ,Timstat3       , NO_HELP           , Timstat3Operators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Tinfo          = {"Tinfo"          ,Tinfo          , NO_HELP           , TinfoOperators          , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction };
-static const module_t module_Tocomplex      = {"Tocomplex"      ,Tocomplex      , NO_HELP           , TocomplexOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Transpose      = {"Transpose"      ,Transpose      , NO_HELP           , TransposeOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Trend          = {"Trend"          ,Trend          , TrendHelp         , TrendOperators          , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
-static const module_t module_Trendarith     = {"Trendarith"     ,Trendarith     , TrendarithHelp    , TrendarithOperators     , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Tstepcount     = {"Tstepcount"     ,Tstepcount     , NO_HELP           , TstepcountOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Unpack         = {"Unpack"         ,Unpack         , UnpackHelp        , UnpackOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vargen         = {"Vargen"         ,Vargen         , VargenHelp        , VargenOperators         , EXPOSED  , CDI_REAL , 0  , 1     , NoRestriction,   {Alias ("for", "seq")} };
-static const module_t module_Varrms         = {"Varrms"         ,Varrms         , NO_HELP           , VarrmsOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Varsstat       = {"Varsstat"       ,Varsstat       , VarsstatHelp      , VarsstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertfillmiss   = {"Vertfillmiss"   ,Vertfillmiss   , VertfillmissHelp  , VertfillmissOperators   , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertintap      = {"Vertintap"      ,Vertintap      , VertintapHelp     , VertintapOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertintgh      = {"Vertintgh"      ,Vertintgh      , VertintghHelp     , VertintghOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertintml      = {"Vertintml"      ,Vertintml      , VertintmlHelp     , VertintmlOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertintzs      = {"Vertintzs"      ,Vertintzs      , NO_HELP           , VertintzsOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertstat       = {"Vertstat"       ,Vertstat       , VertstatHelp      , VertstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertcum        = {"Vertcum"        ,Vertcum        , NO_HELP           , VertcumOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Vertwind       = {"Vertwind"       ,Vertwind       , NO_HELP           , VertwindOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Verifygrid     = {"Verifygrid"     ,Verifygrid     , VerifygridHelp    , VerifygridOperators     , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
-static const module_t module_Verifyweights  = {"Verifyweights"  ,Verifyweights  , NO_HELP           , VerifyweightsOperators  , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
-static const module_t module_Wind           = {"Wind"           ,Wind           , WindHelp          , WindOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Wind2          = {"Wind2"          ,Wind           , Wind2Help         , Wind2Operators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Writegrid      = {"Writegrid"      ,Writegrid      , NO_HELP           , WritegridOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Writerandom    = {"Writerandom"    ,Writerandom    , NO_HELP           , WriterandomOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Yeararith      = {"Yeararith"      ,Yeararith      , YeararithHelp     , YeararithOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Yearmonstat    = {"Yearmonstat"    ,Yearmonstat    , YearmonstatHelp   , YearmonstatOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Ydayarith      = {"Ydayarith"      ,Ydayarith      , YdayarithHelp     , YdayarithOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Ydaypctl       = {"Ydaypctl"       ,Ydaypctl       , YdaypctlHelp      , YdaypctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Ydaystat       = {"Ydaystat"       ,Ydaystat       , YdaystatHelp      , YdaystatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Ydrunpctl      = {"Ydrunpctl"      ,Ydrunpctl      , YdrunpctlHelp     , YdrunpctlOperators      , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Ydrunstat      = {"Ydrunstat"      ,Ydrunstat      , YdrunstatHelp     , YdrunstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Yhourarith     = {"Yhourarith"     ,Yhourarith     , YhourarithHelp    , YhourarithOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Yhourstat      = {"Yhourstat"      ,Yhourstat      , YhourstatHelp     , YhourstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Dhourstat      = {"Dhourstat"      ,Yhourstat      , DhourstatHelp     , DhourstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Ymonarith      = {"Ymonarith"      ,Ymonarith      , YmonarithHelp     , YmonarithOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction,   {Alias ("anomaly","ymonsub")}};
-static const module_t module_Yseasarith     = {"Yseasarith"     ,Ymonarith      , YseasarithHelp    , YseasarithOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Ymoncomp       = {"Ymoncomp"       ,Ymoncomp       , YmoncompHelp      , YmoncompOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Yseascomp      = {"Yseascomp"      ,Ymoncomp       , NO_HELP           , YseascompOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Ymonpctl       = {"Ymonpctl"       ,Ymonpctl       , YmonpctlHelp      , YmonpctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Ymonstat       = {"Ymonstat"       ,Ymonstat       , YmonstatHelp      , YmonstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Yseaspctl      = {"Yseaspctl"      ,Yseaspctl      , YseaspctlHelp     , YseaspctlOperators      , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Yseasstat      = {"Yseasstat"      ,Yseasstat      , YseasstatHelp     , YseasstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Zonstat        = {"Zonstat"        ,Zonstat        , ZonstatHelp       , ZonstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaCfd         = {"EcaCfd"         ,EcaCfd         , EcaCfdHelp        , EcaCfdOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaCsu         = {"EcaCsu"         ,EcaCsu         , EcaCsuHelp        , EcaCsuOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaCwdi        = {"EcaCwdi"        ,EcaCwdi        , EcaCwdiHelp       , EcaCwdiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaCwfi        = {"EcaCwfi"        ,EcaCwfi        , EcaCwfiHelp       , EcaCwfiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaEtr         = {"EcaEtr"         ,EcaEtr         , EcaEtrHelp        , EcaEtrOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaEtccdi      = {"EcaEtccdi"      ,EcaEtccdi      , EcaEtccdiHelp     , EcaEtccdiOperators      , EXPOSED  , CDI_REAL , 3  , 1     , FilesOnly };
-static const module_t module_EcaFd          = {"EcaFd"          ,EcaFd          , EcaFdHelp         , EcaFdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaGsl         = {"EcaGsl"         ,EcaGsl         , EcaGslHelp        , EcaGslOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaHd          = {"EcaHd"          ,EcaHd          , EcaHdHelp         , EcaHdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaHwdi        = {"EcaHwdi"        ,EcaHwdi        , EcaHwdiHelp       , EcaHwdiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaHwfi        = {"EcaHwfi"        ,EcaHwfi        , EcaHwfiHelp       , EcaHwfiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaId          = {"EcaId"          ,EcaId          , EcaIdHelp         , EcaIdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaSu          = {"EcaSu"          ,EcaSu          , EcaSuHelp         , EcaSuOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaTr          = {"EcaTr"          ,EcaTr          , EcaTrHelp         , EcaTrOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaTg10p       = {"EcaTg10p"       ,EcaTg10p       , EcaTg10pHelp      , EcaTg10pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaTg90p       = {"EcaTg90p"       ,EcaTg90p       , EcaTg90pHelp      , EcaTg90pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaTn10p       = {"EcaTn10p"       ,EcaTn10p       , EcaTn10pHelp      , EcaTn10pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaTn90p       = {"EcaTn90p"       ,EcaTn90p       , EcaTn90pHelp      , EcaTn90pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaTx10p       = {"EcaTx10p"       ,EcaTx10p       , EcaTx10pHelp      , EcaTx10pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaTx90p       = {"EcaTx90p"       ,EcaTx90p       , EcaTx90pHelp      , EcaTx90pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaCdd         = {"EcaCdd"         ,EcaCdd         , EcaCddHelp        , EcaCddOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaCwd         = {"EcaCwd"         ,EcaCwd         , EcaCwdHelp        , EcaCwdOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaRr1         = {"EcaRr1"         ,EcaRr1         , EcaRr1Help        , EcaRr1Operators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("eca_r1mm", "eca_rr1")} };
-static const module_t module_EcaPd          = {"EcaPd"          ,EcaPd          , EcaPdHelp         , EcaPdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaR75p        = {"EcaR75p"        ,EcaR75p        , EcaR75pHelp       , EcaR75pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR75ptot     = {"EcaR75ptot"     ,EcaR75ptot     , EcaR75ptotHelp    , EcaR75ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR90p        = {"EcaR90p"        ,EcaR90p        , EcaR90pHelp       , EcaR90pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR90ptot     = {"EcaR90ptot"     ,EcaR90ptot     , EcaR90ptotHelp    , EcaR90ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR95p        = {"EcaR95p"        ,EcaR95p        , EcaR95pHelp       , EcaR95pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR95ptot     = {"EcaR95ptot"     ,EcaR95ptot     , EcaR95ptotHelp    , EcaR95ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR99p        = {"EcaR99p"        ,EcaR99p        , EcaR99pHelp       , EcaR99pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaR99ptot     = {"EcaR99ptot"     ,EcaR99ptot     , EcaR99ptotHelp    , EcaR99ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_EcaRx1day      = {"EcaRx1day"      ,EcaRx1day      , EcaRx1dayHelp     , EcaRx1dayOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaRx5day      = {"EcaRx5day"      ,EcaRx5day      , EcaRx5dayHelp     , EcaRx5dayOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_EcaSdii        = {"EcaSdii"        ,EcaSdii        , EcaSdiiHelp       , EcaSdiiOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Fdns           = {"Fdns"           ,Fdns           , FdnsHelp          , FdnsOperators           , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Strwin         = {"Strwin"         ,Strwin         , StrwinHelp        , StrwinOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Strbre         = {"Strbre"         ,Strbre         , StrbreHelp        , StrbreOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Strgal         = {"Strgal"         ,Strgal         , StrgalHelp        , StrgalOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Hurr           = {"Hurr"           ,Hurr           , HurrHelp          , HurrOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-//static const module_t module_Hi           = {"Hi"             , Hi            , NO_HELP           , HiOperators             , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
-static const module_t module_Wct            = {"Wct"            ,Wct            , WctHelp           , WctOperators            , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
-static const module_t module_Magplot        = {"Magplot"        ,Magplot        , MagplotHelp       , MagplotOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Magvector      = {"Magvector"      ,Magvector      , MagvectorHelp     , MagvectorOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Maggraph       = {"Maggraph"       ,Maggraph       , MaggraphHelp      , MaggraphOperators       , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
-// HIRLAM_EXTENSIONS                          {"
-static const module_t module_Samplegrid     = {"Samplegrid"     ,Samplegrid     , SamplegridHelp    , SamplegridOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_Selmulti       = {"Selmulti"       ,Selmulti       , SelmultiHelp      , SelmultiOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-static const module_t module_WindTrans      = {"WindTrans"      ,WindTrans      , WindTransHelp     , WindTransOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
-// clang-format on
-
-#endif
+//
+//#define NO_HELP {}
+//
+////                                             FuncName          Function         Help                OperatorList              internal                    RestrictionType
+////                                             |                 |                |                   |                         |          cdi-numbertype          |
+////                                             |                 |                |                   |                         |          |         in   out      |
+////                                             |                 |                |                   |                         |          |          |    |       |
+//static const module_t module_Adisit         = {"Adisit"         ,Adisit         , AdisitHelp        , AdisitOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Afterburner    = {"Afterburner"    ,Afterburner    , AfterburnerHelp   , AfterburnerOperators    , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction,   {Alias ("afterburner", "after")}};
+//static const module_t module_Arith          = {"Arith"          ,Arith          , ArithHelp         , ArithOperators          , EXPOSED  , CDI_BOTH , 2  , 1     , NoRestriction };
+//static const module_t module_Arithc         = {"Arithc"         ,Arithc         , ArithcHelp        , ArithcOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Arithdays      = {"Arithdays"      ,Arithdays      , ArithdaysHelp     , ArithdaysOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Arithlat       = {"Arithlat"       ,Arithlat       , ArithlatHelp      , ArithlatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Apply          = {"Apply"          ,nullptr        , ApplyHelp         , {"apply"}               , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Bitrounding    = {"Bitrounding"    ,Bitrounding    , BitroundingHelp   , BitroundingOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Cat            = {"Cat"            ,Cat            , CopyHelp          , CatOperators            , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_CDItest        = {"CDItest"        ,CDItest        , NO_HELP           , CDItestOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_CDIread        = {"CDIread"        ,CDIread        , NO_HELP           , CDIreadOperators        , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
+//static const module_t module_CDIwrite       = {"CDIwrite"       ,CDIwrite       , NO_HELP           , CDIwriteOperators       , EXPOSED  , CDI_REAL , 0  , 1     , NoRestriction };
+//static const module_t module_Change         = {"Change"         ,Change         , ChangeHelp        , ChangeOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("chvar", "chname")} };
+//static const module_t module_Change_e5slm   = {"Change_e5slm"   ,Change_e5slm   , NO_HELP           , Change_e5slmOperators   , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Cloudlayer     = {"Cloudlayer"     ,Cloudlayer     , NO_HELP           , CloudlayerOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_CMOR           = {"CMOR"           ,CMOR           , CMORHelp          , CMOROperators           , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
+//static const module_t module_CMOR_lite      = {"CMORlite"       ,CMOR_lite      , CMORliteHelp      , CMORliteOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_CMOR_table     = {"CMORtable"      ,CMOR_table     , NO_HELP           , CMORtableOperators      , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
+//static const module_t module_Collgrid       = {"Collgrid"       ,Collgrid       , CollgridHelp      , CollgridOperators       , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_Command        = {"Command"        ,Command        , NO_HELP           , CommandOperators        , INTERNAL , CDI_REAL , 1  , 0     , NoRestriction };
+//static const module_t module_Comp           = {"Comp"           ,Comp           , CompHelp          , CompOperators           , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Compc          = {"Compc"          ,Compc          , CompcHelp         , CompcOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Complextorect  = {"Complextorect"  ,Complextorect  , NO_HELP           , ComplextorectOperators  , EXPOSED  , CDI_COMP , 1  , 2     , OnlyFirst     };
+//static const module_t module_Cond           = {"Cond"           ,Cond           , CondHelp          , CondOperators           , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Cond2          = {"Cond2"          ,Cond2          , Cond2Help         , Cond2Operators          , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Condc          = {"Condc"          ,Condc          , CondcHelp         , CondcOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Consecstat     = {"Consecstat"     ,Consecstat     , ConsecstatHelp    , ConsecstatOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Copy           = {"Copy"           ,Copy           , CopyHelp          , CopyOperators           , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_DCW_util       = {"DCW_util"       ,DCW_util       , NO_HELP           , DCW_utilOperators       , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
+//static const module_t module_Dayarith       = {"Dayarith"       ,Dayarith       , DayarithHelp      , DayarithOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Deltat         = {"Deltat"         ,Deltat         , DeltatHelp        , DeltatOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Deltime        = {"Deltime"        ,Deltime        , NO_HELP           , DeltimeOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Depth          = {"Depth"          ,Depth          , NO_HELP           , DepthOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Derivepar      = {"Derivepar"      ,Derivepar      , DeriveparHelp     , DeriveparOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction ,  {Alias ("geopotheight", "gheight")}};
+//static const module_t module_Detrend        = {"Detrend"        ,Detrend        , DetrendHelp       , DetrendOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Diff           = {"Diff"           ,Diff           , DiffHelp          , DiffOperators           , EXPOSED  , CDI_BOTH , 2  , 0     , NoRestriction,   {Alias ("diffv", "diffn")}};
+//static const module_t module_Distgrid       = {"Distgrid"       ,Distgrid       , DistgridHelp      , DistgridOperators       , EXPOSED  , CDI_REAL , 1  , OBASE , NoRestriction };
+//static const module_t module_Duplicate      = {"Duplicate"      ,Duplicate      , DuplicateHelp     , DuplicateOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Echam5ini      = {"Echam5ini"      ,Echam5ini      , NO_HELP           , Echam5iniOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Enlarge        = {"Enlarge"        ,Enlarge        , EnlargeHelp       , EnlargeOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Enlargegrid    = {"Enlargegrid"    ,Enlargegrid    , NO_HELP           , EnlargegridOperators    , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Ensstat        = {"Ensstat"        ,Ensstat        , EnsstatHelp       , EnsstatOperators        , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_Ensstat3       = {"Ensstat3"       ,Ensstat3       , Ensstat2Help      , Ensstat3Operators       , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_Ensval         = {"Ensval"         ,Ensval         , EnsvalHelp        , EnsvalOperators         , EXPOSED  , CDI_REAL , -1 , OBASE , NoRestriction };
+//static const module_t module_Eofcoeff       = {"Eofcoeff"       ,Eofcoeff       , EofcoeffHelp      , EofcoeffOperators       , EXPOSED  , CDI_REAL , 2  , OBASE , NoRestriction };
+//static const module_t module_Eofcoeff3d     = {"Eofcoeff3d"     ,Eofcoeff3d     , EofcoeffHelp      , Eofcoeff3dOperators     , EXPOSED  , CDI_REAL , 2  , OBASE , NoRestriction };
+//static const module_t module_EOFs           = {"EOFs"           ,EOFs           , EOFsHelp          , EOFsOperators           , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
+//static const module_t module_EOF3d          = {"EOF3d"          ,EOF3d          , EOFsHelp          , EOF3dOperators          , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
+//static const module_t module_EstFreq        = {"EstFreq"        ,EstFreq        , NO_HELP           , EstFreqOperators        , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Exprf          = {"Exprf"          ,Exprf          , ExprHelp          , ExprfOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_FC             = {"FC"             ,FC             , NO_HELP           , FCOperators             , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Filedes        = {"Filedes"        ,Filedes        , FiledesHelp       , FiledesOperators        , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction ,  {Alias ("vardes", "codetab")}};
+//static const module_t module_Fillmiss       = {"Fillmiss"       ,Fillmiss       , NO_HELP           , FillmissOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Filter         = {"Filter"         ,Filter         , FilterHelp        , FilterOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Fldrms         = {"Fldrms"         ,Fldrms         , NO_HELP           , FldrmsOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Fldstat        = {"Fldstat"        ,Fldstat        , FldstatHelp       , FldstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("globavg", "fldavg") }};
+//static const module_t module_Fldcor         = {"Fldcor"         ,Fldstat2       , FldcorHelp        , FldcorOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Fldcovar       = {"Fldcovar"       ,Fldstat2       , FldcovarHelp      , FldcovarOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Fourier        = {"Fourier"        ,Fourier        , FourierHelp       , FourierOperators        , EXPOSED  , CDI_COMP , 1  , 1     , NoRestriction };
+//static const module_t module_Gengrid        = {"Gengrid"        ,Gengrid        , NO_HELP           , GengridOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Getgridcell    = {"Getgridcell"    ,Getgridcell    , GetgridcellHelp   , GetgridcellOperators    , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction };
+//static const module_t module_Gradsdes       = {"Gradsdes"       ,Gradsdes       , GradsdesHelp      , GradsdesOperators       , EXPOSED  , CDI_REAL , 1  , 0     , FilesOnly };
+//static const module_t module_Gridboxstat    = {"Gridboxstat"    ,Gridboxstat    , GridboxstatHelp   , GridboxstatOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Gridcell       = {"Gridcell"       ,Gridcell       , GridcellHelp      , GridcellOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Gridsearch     = {"Gridsearch"     ,Gridsearch     , NO_HELP           , GridsearchOperators     , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
+//static const module_t module_Harmonic       = {"Harmonic"       ,Harmonic       , NO_HELP           , HarmonicOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Healpix        = {"Healpix"        ,Healpix        , HealpixHelp       , HealpixOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Histogram      = {"Histogram"      ,Histogram      , HistogramHelp     , HistogramOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Importamsr     = {"Importamsr"     ,Importamsr     , ImportamsrHelp    , ImportamsrOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Importbinary   = {"Importbinary"   ,Importbinary   , ImportbinaryHelp  , ImportbinaryOperators   , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("import_grads", "import_binary")} };
+//static const module_t module_Importcmsaf    = {"Importcmsaf"    ,Importcmsaf    , ImportcmsafHelp   , ImportcmsafOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Importobs      = {"Importobs"      ,Importobs      , NO_HELP           , ImportobsOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Importfv3grid  = {"Importfv3grid"  ,Importfv3grid  , NO_HELP           , Importfv3gridOperators  , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Info           = {"Info"           ,Info           , InfoHelp          , InfoOperators           , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction };
+//static const module_t module_Input          = {"Input"          ,Input          , InputHelp         , InputOperators          , EXPOSED  , CDI_REAL , 0  , 1     , NoRestriction };
+//static const module_t module_Intgrid        = {"Intgrid"        ,Intgrid        , NO_HELP           , IntgridOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction ,  {Alias ("intgrid", "intgridbil")}};
+//static const module_t module_Intgridtraj    = {"Intgridtraj"    ,Intgridtraj    , NO_HELP           , IntgridtrajOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Intlevel       = {"Intlevel"       ,Intlevel       , IntlevelHelp      , IntlevelOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Intlevel3d     = {"Intlevel3d"     ,Intlevel3d     , Intlevel3dHelp    , Intlevel3dOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Inttime        = {"Inttime"        ,Inttime        , InttimeHelp       , InttimeOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Intntime       = {"Intntime"       ,Intntime       , InttimeHelp       , IntntimeOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Intyear        = {"Intyear"        ,Intyear        , IntyearHelp       , IntyearOperators        , EXPOSED  , CDI_REAL , 2  , OBASE , NoRestriction };
+//static const module_t module_Invert         = {"Invert"         ,Invert         , InvertHelp        , InvertOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Invertlev      = {"Invertlev"      ,Invertlev      , InvertlevHelp     , InvertlevOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Selsurface     = {"Selsurface"     ,Selsurface     , SelsurfaceHelp    , SelsurfaceOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Lic            = {"Lic"            ,Lic            , NO_HELP           , LicOperators            , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Longnfo        = {"Longinfo"       ,Longinfo       , NO_HELP           , LonginfoOperators       , EXPOSED  , CDI_REAL , 1 ,  0     , NoRestriction };
+//static const module_t module_MapReduce      = {"MapReduce"      ,MapReduce      , MapReduceHelp     , MapReduceOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Maskbox        = {"Maskbox"        ,Maskbox        , MaskboxHelp       , MaskboxOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Maskregion     = {"Maskregion"     ,Maskbox        , MaskregionHelp    , MaskregionOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Mastrfu        = {"Mastrfu"        ,Mastrfu        , MastrfuHelp       , MastrfuOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Math           = {"Math"           ,Math           , MathHelp          , MathOperators           , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction,   {Alias ("log", "ln")} };
+//static const module_t module_Merge          = {"Merge"          ,Merge          , MergeHelp         , MergeOperators          , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_Mergetime      = {"Mergetime"      ,Mergetime      , MergeHelp         , MergetimeOperators      , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_Mergegrid      = {"Mergegrid"      ,Mergegrid      , MergegridHelp     , MergegridOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Merstat        = {"Merstat"        ,Merstat        , MerstatHelp       , MerstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Monarith       = {"Monarith"       ,Monarith       , MonarithHelp      , MonarithOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Mrotuv         = {"Mrotuv"         ,Mrotuv         , NO_HELP           , MrotuvOperators         , EXPOSED  , CDI_REAL , 1  , 2     , NoRestriction };
+//static const module_t module_Mrotuvb        = {"Mrotuvb"        ,Mrotuvb        , MrotuvbHelp       , MrotuvbOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_NCL_wind       = {"NCL_wind"       ,NCL_wind       , NCL_windHelp      , NCL_windOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Ninfo          = {"Ninfo"          ,Ninfo          , NinfoHelp         , NinfoOperators          , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction,   {Alias ("nvar", "npar")} };
+//static const module_t module_Nmldump        = {"Nmldump"        ,Nmldump        , NO_HELP           , NmldumpOperators        , INTERNAL , CDI_REAL , 0  , 0     , NoRestriction };
+//static const module_t module_Output         = {"Output"         ,Output         , OutputHelp        , OutputOperators         , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction };
+//static const module_t module_Outputtab      = {"Outputtab"      ,Output         , OutputtabHelp     , OutputtabOperators      , EXPOSED  , CDI_REAL , -1 , 0     , NoRestriction,   {Alias ("outputkey", "outputtab")} };
+//static const module_t module_Outputgmt      = {"Outputgmt"      ,Outputgmt      , OutputgmtHelp     , OutputgmtOperators      , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction,   {Alias ("outputcenter", "gmtxyz"), Alias("outputbounds", "gmtcells")} };
+//static const module_t module_Pack           = {"Pack"           ,Pack           , PackHelp          , PackOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Pardup         = {"Pardup"         ,Pardup         , NO_HELP           , PardupOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Pinfo          = {"Pinfo"          ,Pinfo          , NO_HELP           , PinfoOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Query          = {"Query"          ,Query          , NO_HELP           , QueryOperators          , EXPOSED  , CDI_REAL , 1  , 1     , FilesOnly };
+//static const module_t module_Pressure       = {"Pressure"       ,Pressure       , NO_HELP           , PressureOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("deltap_fl","deltap"),Alias("fpressure", "pressure_fl"), Alias("hpressure", "pressure_hl")}};
+//static const module_t module_Recttocomplex  = {"Recttocomplex"  ,Recttocomplex  , NO_HELP           , RecttocomplexOperators  , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Regres         = {"Regres"         ,Regres         , RegresHelp        , RegresOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remap          = {"Remap"          ,Remapgrid      , RemapHelp         , RemapOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapbil       = {"Remapbil"       ,Remapgrid      , RemapbilHelp      , RemapbilOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapbic       = {"Remapbic"       ,Remapgrid      , RemapbicHelp      , RemapbicOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapnn        = {"Remapnn"        ,Remapgrid      , RemapnnHelp       , RemapnnOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapdis       = {"Remapdis"       ,Remapgrid      , RemapdisHelp      , RemapdisOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapcon       = {"Remapcon"       ,Remapgrid      , RemapconHelp      , RemapconOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("remapycon", "remapcon")} };
+//static const module_t module_Remapycon2     = {"Remapycon2"     ,Remapgrid      , NO_HELP           , Remapycon2Operators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapscon      = {"Remapscon"      ,Remapgrid      , NO_HELP           , RemapsconOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapscon2     = {"Remapscon2"     ,Remapgrid      , NO_HELP           , Remapscon2Operators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remaplaf       = {"Remaplaf"       ,Remapgrid      , RemaplafHelp      , RemaplafOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapavg       = {"Remapavg"       ,Remapgrid      , NO_HELP           , RemapavgOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Genbil         = {"Genbil"         ,Remapweights   , RemapbilHelp      , GenbilOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Genbic         = {"Genbic"         ,Remapweights   , RemapbicHelp      , GenbicOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Gennn          = {"Gennn"          ,Remapweights   , RemapnnHelp       , GennnOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Gendis         = {"Gendis"         ,Remapweights   , RemapdisHelp      , GendisOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Gencon         = {"Gencon"         ,Remapweights   , RemapconHelp      , GenconOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias("genycon", "gencon")} };
+//static const module_t module_Genycon2       = {"Genycon2"       ,Remapweights   , NO_HELP           , Genycon2Operators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Genscon        = {"Genscon"        ,Remapweights   , NO_HELP           , GensconOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Genscon2       = {"Genscon2"       ,Remapweights   , NO_HELP           , Genscon2Operators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Genlaf         = {"Genlaf"         ,Remapweights   , RemaplafHelp      , GenlafOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapstat      = {"Remapstat"      ,Remapstat      , RemapstatHelp     , RemapstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Remapeta       = {"Remapeta"       ,Remapeta       , RemapetaHelp      , RemapetaOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Replace        = {"Replace"        ,Replace        , ReplaceHelp       , ReplaceOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Replacevalues  = {"Replacevalues"  ,Replacevalues  , ReplacevaluesHelp , ReplacevaluesOperators  , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Rhopot         = {"Rhopot"         ,Rhopot         , RhopotHelp        , RhopotOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Rotuv          = {"Rotuv"          ,Rotuv          , RotuvbHelp        , RotuvOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Runpctl        = {"Runpctl"        ,Runpctl        , RunpctlHelp       , RunpctlOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Runstat        = {"Runstat"        ,Runstat        , RunstatHelp       , RunstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Samplegridicon = {"Samplegridicon" ,Samplegridicon , NO_HELP           , SamplegridiconOperators , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
+//static const module_t module_Seascount      = {"Seascount"      ,Seascount      , NO_HELP           , SeascountOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Seaspctl       = {"Seaspctl"       ,Seaspctl       , SeaspctlHelp      , SeaspctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Seasstat       = {"Seasstat"       ,Seasstat       , SeasstatHelp      , SeasstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Seasmonstat    = {"Seasmonstat"    ,Seasmonstat    , NO_HELP           , SeasmonstatOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Selbox         = {"Selbox"         ,Selbox         , SelboxHelp        , SelboxOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Selgridcell    = {"Selgridcell"    ,Selgridcell    , SelgridcellHelp   , SelgridcellOperators    , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Select         = {"Select"         ,Select         , SelectHelp        , SelectOperators         , EXPOSED  , CDI_BOTH , -1 , 1     , NoRestriction };
+//static const module_t module_Selvar         = {"Selvar"         ,Selvar         , SelvarHelp        , SelvarOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction,   {Alias ("selvar", "selname"), Alias("delvar"  , "delname"),Alias("selgridname"     , "selgrid")} };
+//static const module_t module_Selrec         = {"Selrec"         ,Selrec         , SelvarHelp        , SelrecOperators         , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Selregion      = {"Selregion"      ,Selregion      , SelregionHelp     , SelregionOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Seloperator    = {"Seloperator"    ,Seloperator    , NO_HELP           , SeloperatorOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Seltime        = {"Seltime"        ,Seltime        , SeltimeHelp       , SeltimeOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction,   {Alias ("selseas", "selseason"), Alias("selmon", "selmonth")} };
+//static const module_t module_Selyearidx     = {"Selyearidx"     ,Selyearidx     , SelyearidxHelp    , SelyearidxOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Set            = {"Set"            ,Set            , SetHelp           , SetOperators            , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction ,  {Alias ("setvar", "setname")}};
+//static const module_t module_Setattribute   = {"Setattribute"   ,Setattribute   , SetattributeHelp  , SetattributeOperators   , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Setbox         = {"Setbox"         ,Setbox         , SetboxHelp        , SetboxOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Setgrid        = {"Setgrid"        ,Setgrid        , SetgridHelp       , SetgridOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Setgridcell    = {"Setgridcell"    ,Setgridcell    , SetgridcellHelp   , SetgridcellOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Sethalo        = {"Sethalo"        ,Sethalo        , SethaloHelp       , SethaloOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Setmiss        = {"Setmiss"        ,Setmiss        , SetmissHelp       , SetmissOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Setmisstonn    = {"Setmisstonn"    ,Fillmiss       , SetmissHelp       , SetmisstonnOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Setcodetab     = {"Setcodetab"     ,Setpartab      , SetHelp           , SetcodetabOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction ,  {Alias ("setpartab", "setcodetab")}};
+//static const module_t module_Setpartab      = {"Setpartab"      ,Setpartab      , SetpartabHelp     , SetpartabOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("setpartabv", "setpartabn")} };
+//static const module_t module_Setrcaname     = {"Setrcaname"     ,Setrcaname     , NO_HELP           , SetrcanameOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Settime        = {"Settime"        ,Settime        , SettimeHelp       , SettimeOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Setzaxis       = {"Setzaxis"       ,Setzaxis       , SetzaxisHelp      , SetzaxisOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Shiftxy        = {"Shiftxy"        ,Shiftxy        , ShiftxyHelp       , ShiftxyOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Showinfo       = {"Showinfo"       ,Showinfo       , ShowinfoHelp      , ShowinfoOperators       , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction ,  {Alias ("showvar", "showname")} };
+//static const module_t module_Showattribute  = {"Showattribute"  ,Showattribute  , ShowattributeHelp , ShowattributeOperators  , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
+//static const module_t module_Sinfo          = {"Sinfo"          ,Sinfo          , SinfoHelp         , SinfoOperators          , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction,   {Alias ("infov", "infon"),Alias("sinfov", "sinfon")} };
+//static const module_t module_XSinfo         = {"XSinfo"         ,Sinfo          , XSinfoHelp        , XSinfoOperators         , EXPOSED  , CDI_BOTH , -1 , 0     , NoRestriction };
+//static const module_t module_Smooth         = {"Smooth"         ,Smooth         , SmoothHelp        , SmoothOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Sort           = {"Sort"           ,Sort           , NO_HELP           , SortOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("sortvar", "sortname")} };
+//static const module_t module_Sorttimestamp  = {"Sorttimestamp"  ,Sorttimestamp  , NO_HELP           , SorttimestampOperators  , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//static const module_t module_Specinfo       = {"Specinfo"       ,Specinfo       , NO_HELP           , SpecinfoOperators       , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
+//static const module_t module_Spectral       = {"Spectral"       ,Spectral       , SpectralHelp      , SpectralOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Specconv       = {"Specconv"       ,Spectral       , SpecconvHelp      , SpecconvOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Spectrum       = {"Spectrum"       ,Spectrum       , NO_HELP           , SpectrumOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Split          = {"Split"          ,Split          , SplitHelp         , SplitOperators          , EXPOSED  , CDI_BOTH , 1  , OBASE , NoRestriction,   {Alias ("splitvar", "splitname")} };
+//static const module_t module_Splitdate      = {"Splitdate"      ,Splitdate      , SplitdateHelp     , SplitdateOperators      , EXPOSED  , CDI_BOTH , 1  , OBASE , OnlyFirst };
+//static const module_t module_Splitrec       = {"Splitrec"       ,Splitrec       , SplitHelp         , SplitrecOperators       , EXPOSED  , CDI_BOTH , 1  , OBASE , NoRestriction };
+//static const module_t module_Splitsel       = {"Splitsel"       ,Splitsel       , SplitselHelp      , SplitselOperators       , EXPOSED  , CDI_BOTH , 1  , OBASE , OnlyFirst };
+//static const module_t module_Splittime      = {"Splittime"      ,Splittime      , SplittimeHelp     , SplittimeOperators      , EXPOSED  , CDI_BOTH , 1  , OBASE , OnlyFirst };
+//static const module_t module_Splityear      = {"Splityear"      ,Splityear      , SplittimeHelp     , SplityearOperators      , EXPOSED  , CDI_BOTH , 1  , OBASE , NoRestriction };
+//static const module_t module_Tee            = {"Tee"            ,Tee            , TeeHelp           , TeeOperators            , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Testdata       = {"Testdata"       ,Testdata       , NO_HELP           , TestdataOperators       , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Tests          = {"Tests"          ,Tests          , NO_HELP           , TestsOperators          , INTERNAL , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Timcount       = {"Timcount"       ,Timcount       , NO_HELP           , TimcountOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Yearcount      = {"Yearcount"      ,Timcount       , NO_HELP           , YearcountOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Moncount       = {"Moncount"       ,Timcount       , NO_HELP           , MoncountOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Daycount       = {"Daycount"       ,Timcount       , NO_HELP           , DaycountOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Hourcount      = {"Hourcount"      ,Timcount       , NO_HELP           , HourcountOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Timcumsum      = {"Timcumsum"      ,Timcumsum      , TimcumsumHelp     , TimcumsumOperators      , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Timpctl        = {"Timpctl"        ,Timpctl        , TimpctlHelp       , TimpctlOperators        , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Yearpctl       = {"Yearpctl"       ,Timpctl        , YearpctlHelp      , YearpctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Monpctl        = {"Monpctl"        ,Timpctl        , MonpctlHelp       , MonpctlOperators        , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Daypctl        = {"Daypctl"        ,Timpctl        , DaypctlHelp       , DaypctlOperators        , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Hourpctl       = {"Hourpctl"       ,Timpctl        , HourpctlHelp      , HourpctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Timselpctl     = {"Timselpctl"     ,Timselpctl     , TimselpctlHelp    , TimselpctlOperators     , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Timfillmiss    = {"Timfillmiss"    ,Timfillmiss    , TimfillmissHelp   , TimfillmissOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Timsort        = {"Timsort"        ,Timsort        , TimsortHelp       , TimsortOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("sort", "timsort")} };
+//static const module_t module_Timselstat     = {"Timselstat"     ,Timselstat     , TimselstatHelp    , TimselstatOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_XTimstat       = {"XTimstat"       ,XTimstat       , NO_HELP           , XTimstatOperators       , INTERNAL , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Timstat        = {"Timstat"        ,Timstat        , TimstatHelp       , TimstatOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Yearstat       = {"Yearstat"       ,Timstat        , YearstatHelp      , YearstatOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Monstat        = {"Monstat"        ,Timstat        , MonstatHelp       , MonstatOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Daystat        = {"Daystat"        ,Timstat        , DaystatHelp       , DaystatOperators        , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Hourstat       = {"Hourstat"       ,Timstat        , HourstatHelp      , HourstatOperators       , EXPOSED  , CDI_BOTH , 1  , 1     , NoRestriction };
+//static const module_t module_Timcor         = {"Timcor"         ,Timstat2       , TimcorHelp        , TimcorOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Timcovar       = {"Timcovar"       ,Timstat2       , TimcovarHelp      , TimcovarOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Timrmsd        = {"Timrmsd"        ,Timstat2       , NO_HELP           , TimrmsdOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Timstat3       = {"Timstat3"       ,Timstat3       , NO_HELP           , Timstat3Operators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Tinfo          = {"Tinfo"          ,Tinfo          , NO_HELP           , TinfoOperators          , EXPOSED  , CDI_BOTH , 1  , 0     , NoRestriction };
+//static const module_t module_Tocomplex      = {"Tocomplex"      ,Tocomplex      , NO_HELP           , TocomplexOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Transpose      = {"Transpose"      ,Transpose      , NO_HELP           , TransposeOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Trend          = {"Trend"          ,Trend          , TrendHelp         , TrendOperators          , EXPOSED  , CDI_REAL , 1  , 2     , OnlyFirst     };
+//static const module_t module_Trendarith     = {"Trendarith"     ,Trendarith     , TrendarithHelp    , TrendarithOperators     , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Tstepcount     = {"Tstepcount"     ,Tstepcount     , NO_HELP           , TstepcountOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Unpack         = {"Unpack"         ,Unpack         , UnpackHelp        , UnpackOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vargen         = {"Vargen"         ,Vargen         , VargenHelp        , VargenOperators         , EXPOSED  , CDI_REAL , 0  , 1     , NoRestriction,   {Alias ("for", "seq")} };
+//static const module_t module_Varrms         = {"Varrms"         ,Varrms         , NO_HELP           , VarrmsOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Varsstat       = {"Varsstat"       ,Varsstat       , VarsstatHelp      , VarsstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertfillmiss   = {"Vertfillmiss"   ,Vertfillmiss   , VertfillmissHelp  , VertfillmissOperators   , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertintap      = {"Vertintap"      ,Vertintap      , VertintapHelp     , VertintapOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertintgh      = {"Vertintgh"      ,Vertintgh      , VertintghHelp     , VertintghOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertintml      = {"Vertintml"      ,Vertintml      , VertintmlHelp     , VertintmlOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertintzs      = {"Vertintzs"      ,Vertintzs      , NO_HELP           , VertintzsOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertstat       = {"Vertstat"       ,Vertstat       , VertstatHelp      , VertstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertcum        = {"Vertcum"        ,Vertcum        , NO_HELP           , VertcumOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Vertwind       = {"Vertwind"       ,Vertwind       , NO_HELP           , VertwindOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Verifygrid     = {"Verifygrid"     ,Verifygrid     , VerifygridHelp    , VerifygridOperators     , EXPOSED  , CDI_REAL , 1  , 0     , NoRestriction };
+//static const module_t module_Verifyweights  = {"Verifyweights"  ,Verifyweights  , NO_HELP           , VerifyweightsOperators  , EXPOSED  , CDI_REAL , 0  , 0     , NoRestriction };
+//static const module_t module_Wind           = {"Wind"           ,Wind           , WindHelp          , WindOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Wind2          = {"Wind2"          ,Wind           , Wind2Help         , Wind2Operators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Writegrid      = {"Writegrid"      ,Writegrid      , NO_HELP           , WritegridOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Writerandom    = {"Writerandom"    ,Writerandom    , NO_HELP           , WriterandomOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Yeararith      = {"Yeararith"      ,Yeararith      , YeararithHelp     , YeararithOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Yearmonstat    = {"Yearmonstat"    ,Yearmonstat    , YearmonstatHelp   , YearmonstatOperators    , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Ydayarith      = {"Ydayarith"      ,Ydayarith      , YdayarithHelp     , YdayarithOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Ydaypctl       = {"Ydaypctl"       ,Ydaypctl       , YdaypctlHelp      , YdaypctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Ydaystat       = {"Ydaystat"       ,Ydaystat       , YdaystatHelp      , YdaystatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Ydrunpctl      = {"Ydrunpctl"      ,Ydrunpctl      , YdrunpctlHelp     , YdrunpctlOperators      , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Ydrunstat      = {"Ydrunstat"      ,Ydrunstat      , YdrunstatHelp     , YdrunstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Yhourarith     = {"Yhourarith"     ,Yhourarith     , YhourarithHelp    , YhourarithOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Yhourstat      = {"Yhourstat"      ,Yhourstat      , YhourstatHelp     , YhourstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Dhourstat      = {"Dhourstat"      ,Yhourstat      , DhourstatHelp     , DhourstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Ymonarith      = {"Ymonarith"      ,Ymonarith      , YmonarithHelp     , YmonarithOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction,   {Alias ("anomaly","ymonsub")}};
+//static const module_t module_Yseasarith     = {"Yseasarith"     ,Ymonarith      , YseasarithHelp    , YseasarithOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Ymoncomp       = {"Ymoncomp"       ,Ymoncomp       , YmoncompHelp      , YmoncompOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Yseascomp      = {"Yseascomp"      ,Ymoncomp       , NO_HELP           , YseascompOperators      , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Ymonpctl       = {"Ymonpctl"       ,Ymonpctl       , YmonpctlHelp      , YmonpctlOperators       , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Ymonstat       = {"Ymonstat"       ,Ymonstat       , YmonstatHelp      , YmonstatOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Yseaspctl      = {"Yseaspctl"      ,Yseaspctl      , YseaspctlHelp     , YseaspctlOperators      , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Yseasstat      = {"Yseasstat"      ,Yseasstat      , YseasstatHelp     , YseasstatOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Zonstat        = {"Zonstat"        ,Zonstat        , ZonstatHelp       , ZonstatOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaCfd         = {"EcaCfd"         ,EcaCfd         , EcaCfdHelp        , EcaCfdOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaCsu         = {"EcaCsu"         ,EcaCsu         , EcaCsuHelp        , EcaCsuOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaCwdi        = {"EcaCwdi"        ,EcaCwdi        , EcaCwdiHelp       , EcaCwdiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaCwfi        = {"EcaCwfi"        ,EcaCwfi        , EcaCwfiHelp       , EcaCwfiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaEtr         = {"EcaEtr"         ,EcaEtr         , EcaEtrHelp        , EcaEtrOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaEtccdi      = {"EcaEtccdi"      ,EcaEtccdi      , EcaEtccdiHelp     , EcaEtccdiOperators      , EXPOSED  , CDI_REAL , 3  , 1     , FilesOnly };
+//static const module_t module_EcaFd          = {"EcaFd"          ,EcaFd          , EcaFdHelp         , EcaFdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaGsl         = {"EcaGsl"         ,EcaGsl         , EcaGslHelp        , EcaGslOperators         , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaHd          = {"EcaHd"          ,EcaHd          , EcaHdHelp         , EcaHdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaHwdi        = {"EcaHwdi"        ,EcaHwdi        , EcaHwdiHelp       , EcaHwdiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaHwfi        = {"EcaHwfi"        ,EcaHwfi        , EcaHwfiHelp       , EcaHwfiOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaId          = {"EcaId"          ,EcaId          , EcaIdHelp         , EcaIdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaSu          = {"EcaSu"          ,EcaSu          , EcaSuHelp         , EcaSuOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaTr          = {"EcaTr"          ,EcaTr          , EcaTrHelp         , EcaTrOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaTg10p       = {"EcaTg10p"       ,EcaTg10p       , EcaTg10pHelp      , EcaTg10pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaTg90p       = {"EcaTg90p"       ,EcaTg90p       , EcaTg90pHelp      , EcaTg90pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaTn10p       = {"EcaTn10p"       ,EcaTn10p       , EcaTn10pHelp      , EcaTn10pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaTn90p       = {"EcaTn90p"       ,EcaTn90p       , EcaTn90pHelp      , EcaTn90pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaTx10p       = {"EcaTx10p"       ,EcaTx10p       , EcaTx10pHelp      , EcaTx10pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaTx90p       = {"EcaTx90p"       ,EcaTx90p       , EcaTx90pHelp      , EcaTx90pOperators       , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaCdd         = {"EcaCdd"         ,EcaCdd         , EcaCddHelp        , EcaCddOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaCwd         = {"EcaCwd"         ,EcaCwd         , EcaCwdHelp        , EcaCwdOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaRr1         = {"EcaRr1"         ,EcaRr1         , EcaRr1Help        , EcaRr1Operators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction,   {Alias ("eca_r1mm", "eca_rr1")} };
+//static const module_t module_EcaPd          = {"EcaPd"          ,EcaPd          , EcaPdHelp         , EcaPdOperators          , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaR75p        = {"EcaR75p"        ,EcaR75p        , EcaR75pHelp       , EcaR75pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR75ptot     = {"EcaR75ptot"     ,EcaR75ptot     , EcaR75ptotHelp    , EcaR75ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR90p        = {"EcaR90p"        ,EcaR90p        , EcaR90pHelp       , EcaR90pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR90ptot     = {"EcaR90ptot"     ,EcaR90ptot     , EcaR90ptotHelp    , EcaR90ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR95p        = {"EcaR95p"        ,EcaR95p        , EcaR95pHelp       , EcaR95pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR95ptot     = {"EcaR95ptot"     ,EcaR95ptot     , EcaR95ptotHelp    , EcaR95ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR99p        = {"EcaR99p"        ,EcaR99p        , EcaR99pHelp       , EcaR99pOperators        , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaR99ptot     = {"EcaR99ptot"     ,EcaR99ptot     , EcaR99ptotHelp    , EcaR99ptotOperators     , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_EcaRx1day      = {"EcaRx1day"      ,EcaRx1day      , EcaRx1dayHelp     , EcaRx1dayOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaRx5day      = {"EcaRx5day"      ,EcaRx5day      , EcaRx5dayHelp     , EcaRx5dayOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_EcaSdii        = {"EcaSdii"        ,EcaSdii        , EcaSdiiHelp       , EcaSdiiOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Fdns           = {"Fdns"           ,Fdns           , FdnsHelp          , FdnsOperators           , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Strwin         = {"Strwin"         ,Strwin         , StrwinHelp        , StrwinOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Strbre         = {"Strbre"         ,Strbre         , StrbreHelp        , StrbreOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Strgal         = {"Strgal"         ,Strgal         , StrgalHelp        , StrgalOperators         , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Hurr           = {"Hurr"           ,Hurr           , HurrHelp          , HurrOperators           , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+////static const module_t module_Hi           = {"Hi"             , Hi            , NO_HELP           , HiOperators             , EXPOSED  , CDI_REAL , 3  , 1     , NoRestriction };
+//static const module_t module_Wct            = {"Wct"            ,Wct            , WctHelp           , WctOperators            , EXPOSED  , CDI_REAL , 2  , 1     , NoRestriction };
+//static const module_t module_Magplot        = {"Magplot"        ,Magplot        , MagplotHelp       , MagplotOperators        , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Magvector      = {"Magvector"      ,Magvector      , MagvectorHelp     , MagvectorOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Maggraph       = {"Maggraph"       ,Maggraph       , MaggraphHelp      , MaggraphOperators       , EXPOSED  , CDI_REAL , -1 , 1     , NoRestriction };
+//// HIRLAM_EXTENSIONS                          {"
+//static const module_t module_Samplegrid     = {"Samplegrid"     ,Samplegrid     , SamplegridHelp    , SamplegridOperators     , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_Selmulti       = {"Selmulti"       ,Selmulti       , SelmultiHelp      , SelmultiOperators       , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//static const module_t module_WindTrans      = {"WindTrans"      ,WindTrans      , WindTransHelp     , WindTransOperators      , EXPOSED  , CDI_REAL , 1  , 1     , NoRestriction };
+//  clang-format on
+//
+//#endif
diff --git a/src/module_static_maps.cc b/src/module_static_maps.cc
deleted file mode 100644
index 552bff45e1dd4d2a5a8a0ef3d5d3022e87b5efd7..0000000000000000000000000000000000000000
--- a/src/module_static_maps.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "module_static_maps.h"
-#include "modules.h"
-
-std::map<std::string, module_t> &
-get_modules()
-{
-  static std::map<std::string, module_t> modules;
-  return modules;
-};
-
-std::map<std::string, std::string> &
-get_aliases()
-{
-  static std::map<std::string, std::string> aliases;
-  return aliases;
-}
-
-std::map<std::string, std::string> &
-get_module_map()
-{
-  static std::map<std::string, std::string> modules_map;
-  return modules_map;
-}
diff --git a/src/module_static_maps.h b/src/module_static_maps.h
deleted file mode 100644
index b26e0165012d924cb5592589ddf9ea7fdd6a77ce..0000000000000000000000000000000000000000
--- a/src/module_static_maps.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MODULE_STATIC_MAPS_H
-#define MODULE_STATIC_MAPS_H
-
-#include <map>
-#include <string>
-
-struct module_t;
-
-/*
- * This file serves no purpose but to make these "hidden" static definitions more visible for
- * future developers.
- */
-
-/**
- * ==========================================================================================
- * Functions Containing static structures for static initaializaton of modules,
- * aliases and operators.  All static maps contained within the following
- * functions are necessary to not encounter problems with the non guaranteed
- * order of initalization of static variables.
- * ==========================================================================================
- */
-std::map<std::string, module_t> &get_modules();
-
-std::map<std::string, std::string> &get_aliases();
-
-std::map<std::string, std::string> &get_module_map();
-//==========================================================================================
-//
-#endif
diff --git a/src/modules.cc b/src/modules.cc
index 995f480b7b3d4fd33ef6c30e9b29b16cf8861d1b..a0bcad121655df52331fff785ebffd92cf521079 100644
--- a/src/modules.cc
+++ b/src/modules.cc
@@ -1,4 +1,5 @@
 /*
+ * #include "util_string.h"
   This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
 
   Author: Uwe Schulzweida
@@ -7,14 +8,6 @@
 */
 
 #include "modules.h"
-#include "util_string.h"
-#include "cdo_output.h"
-
-#include <cstring>
-#include <algorithm>  // for std::sort()
-
-int add_alias(const std::string &alias, const std::string &original);
-void add_module(const std::string &module_name, const module_t &new_module);
 
 /* removes '-' from operator string and returns copy of parameter operatorCommand */
 /***
@@ -25,560 +18,3 @@ std::vector<void *> custom_modules_lib_handles;
 /***
   Contains added modules as values and their names as key
   */
-
-Alias::Alias(const std::string &_alias, const std::string &_original) : alias(_alias), original(_original) {}
-
-// clang-format off
-module_t::module_t(const std::string &p_mod_name,
-                   void *(*p_func)(void *),
-                   const char **p_help,
-                   const std::vector<std::string> &p_opers,
-                   short p_m, short p_n, short p_siC, short p_soC,
-                   PositionRestrictions p_pos_restriction,
-                   const std::vector<Alias> &p_aliases)
-
-                 : mod_name(p_mod_name),
-                   func(p_func),
-                   help(p_help),
-                   mode(p_m),
-                   number(p_n),
-                   operators(p_opers),
-                   constraints({p_siC, p_soC, p_pos_restriction}),
-                   aliases(p_aliases)
-{
-  add_module(p_mod_name, *this);
-  for (const auto &al : aliases) add_alias(al.alias, al.original);
-}
-// clang-format on
-
-std::string
-module_t::toString()
-{
-  std::string inp = (get_stream_in_cnt() >= 0) ? std::to_string(get_stream_in_cnt()) : "Arbitrary";
-  std::string out = (get_stream_out_cnt() >= 0) ? std::to_string(get_stream_out_cnt()) : "Output base";
-  std::string restriction = "none";
-
-  if (get_pos_restriction() == OnlyFirst) restriction = "Can only be the first operator";
-  if (get_pos_restriction() == FilesOnly)
-    (restriction != Green("none")) ? restriction = Yellow("Can only use files as input.")
-                                   : restriction += Yellow(", Can only use files as input");
-
-  std::string desc = "Input: " + inp + ", Ouput: " + out + ", Restricton: " + restriction;
-
-  return desc;
-}
-
-void
-extract_name_and_argument(const std::string &command, std::string &operatorName, std::string &operatorArgument)
-{
-  constexpr char delimiter = ',';
-
-  const size_t start = (command[0] == '-') ? 1 : 0;
-
-  size_t len = command.find(delimiter);
-  if (len == std::string::npos)
-    {
-      len = command.size();
-      operatorArgument = "";
-    }
-  else { operatorArgument = command.substr(len + 1, std::string::npos); }
-
-  operatorName = command.substr(start, len - start);
-}
-
-std::string
-extract_operator_name(const std::string &operatorCommand)
-{
-  constexpr char delimiter = ',';
-
-  const size_t start = (operatorCommand[0] == '-') ? 1 : 0;
-
-  size_t len = operatorCommand.find(delimiter);
-  if (len == std::string::npos) len = operatorCommand.size();
-
-  std::string oper_name = operatorCommand.substr(start, len - start);
-  return oper_name;
-}
-
-bool
-is_alias(const std::string &operatorName)
-{
-  return (get_aliases().find(operatorName) != get_aliases().end());
-}
-
-std::string
-get_original(const std::string &operatorName)
-{
-  std::string name = extract_operator_name(operatorName);
-  return (is_alias(name) ? get_aliases()[name] : name);
-}
-/**
- * @param a pointer to a string/substring
- * @param b pointer to a string/substring
- * @param alen length of string a
- * @param blen length of string b
- * @retval true if a is similar to b
- * @retval false if a is not similar to b
- *
- * Recursive function for finding substrings of a operator name that match other operators.
- */
-static bool
-similar(const char *a, const char *b, unsigned long alen, unsigned long blen)
-{
-  if (alen > 2 && blen > 2 && strstr(b, a)) return true;
-
-  while (*a && *b && *a == *b)
-    {
-      a++;
-      b++;
-    }
-  if (!*a && !*b) return true;
-
-  //  printf("%d %d %s %s\n", alen, blen, a, b);
-
-  if (alen >= 2 && blen >= 1 && *a && similar(a + 1, b, alen - 2, blen - 1)) return true;
-
-  if (alen >= 1 && blen >= 2 && *b && similar(a, b + 1, alen - 1, blen - 2)) return true;
-
-  return false;
-}
-
-/**
- * @param original string tested for similarity to \p other
- * @param other string that \p original will be compared to
- * @retval true if original and other are similar
- * @retval false if not
- *
- * Wrapper function for #similar() to parse c++ strings to c strings
- */
-static bool
-similar(const std::string &original, const std::string &other)
-{
-  return (similar(original.c_str(), other.c_str(), original.size(), other.size()));
-}
-
-/**
- * @param operatorName operator name
- * @retval true if #modules_map contains \p operatorName
- * @retval false if not
- */
-bool
-operator_name_exists(const std::string &operatorName)
-{
-  if (get_module_map().find(operatorName) != get_module_map().end()) return true;
-
-  if (get_aliases().find(operatorName) != get_aliases().end()) return true;
-
-  return false;
-}
-
-/***
- * function for finding similar operator names for the given string
- * @param operatorName operator name to find similar operators for
- * @returns A string with all found names. The string is seqmented into lines
- * with a max length of 75 characters
- */
-std::string
-find_similar_operators(const std::string &operatorName)
-{
-  std::string found_similar_operators = "";
-  size_t lines = 1;
-  constexpr size_t line_length = 105;
-
-  if (operatorName != "")
-    {
-      // Searching for similar operator names in operator to module map
-      for (const auto &str : get_module_map())
-        {
-          if (similar(string_to_lower(operatorName), str.first))
-            {
-              if (found_similar_operators.size() + str.first.size() > lines * line_length)
-                {
-                  found_similar_operators += "\n";
-                  lines++;
-                }
-              found_similar_operators += str.first;
-              found_similar_operators += " ";
-            }
-        }
-      // Searching for similar operator names in aliases to original map
-      for (const auto &str : get_aliases())
-        {
-          if (similar(string_to_lower(operatorName), str.first))
-            {
-              if (found_similar_operators.size() + str.first.size() > lines * line_length)
-                {
-                  found_similar_operators += "\n";
-                  lines++;
-                }
-              found_similar_operators += str.first;
-              found_similar_operators += " ";
-            }
-        }
-    }
-
-  return found_similar_operators;
-}
-
-/**
- * @param operatorName operator name.
- * @retval true if \p operatorName exists.
- * @retval false if \p operatorName is not in #modules_map
- *
- * Checks if given \p operatorName is in #modules_map. Else returns false.
-
- * Checks if \p operatorName is not a file.
-
- * If no matching operator is found checks for similar operators using find_similar_operators().
- *
- *  \note If \p operatorName is a file name the program will exit.
- */
-static bool
-check_operator(const std::string &operatorName)
-{
-  if (operator_name_exists(operatorName)) { return true; }
-  else if (operatorName == "") { cdo_abort("Operator name missing!"); }
-  else
-    {
-      // Checking if the operatorname is an existing file name
-      auto fp = std::fopen(operatorName.c_str(), "r");
-      if (fp)
-        {
-          std::fclose(fp);
-          fprintf(stderr, "Use commandline option -h for help.\n");
-          cdo_abort("Operator missing, %s is a file on disk!", operatorName);
-        }
-      // Operator is no filename
-      // Checking for similar operators
-      fprintf(stderr, "Operator >%s< not found!\n", operatorName.c_str());
-      fprintf(stderr, "Similar operators are:\n");
-      const auto found_similar_operators = find_similar_operators(operatorName);
-
-      if (found_similar_operators.size() > 0) { std::cerr << found_similar_operators << std::endl; }
-      else { fprintf(stderr, "(not found)\n"); }
-
-      exit(EXIT_FAILURE);
-    }
-
-  return false;
-}
-
-/***
- * Adds a module and its operators to cdo.
- * Adds the module to modules
- * Adds the operators of modules to modules_map
- * @param new_module newly constructed module
- * @note: if an error happens while adding the new module cdo will exit.
- */
-void
-add_module(const std::string &module_name, const module_t &new_module)
-{
-  if (get_modules().find(module_name) == get_modules().end())
-    {
-      get_modules()[module_name] = new_module;
-      for (const auto &operatorName : new_module.operators)
-        {
-          // if the operator name is not already in the map or in the aliases
-          if (!operator_name_exists(operatorName)) { get_module_map()[operatorName] = module_name; }
-          else { cdo_abort("Tried to add operator but the operator name already exists"); }
-        }
-    }
-  else { cdo_abort("Module %s name already exists", module_name); }
-}
-
-/**
- * adds an key value pair to #modules_map with alias as key and originals name as value
- * @param alias new alias to be added
- * @param original original operator name
- */
-int
-add_alias(const std::string &alias, const std::string &original)
-{
-  auto iter_original = get_module_map().find(original);
-  auto iter_alias = get_aliases().find(alias);
-
-  if (iter_alias != get_aliases().end())
-    {
-      printf("WARNING: alias %s could not be added: it already exists\n", alias.c_str());
-      return -1;
-    }
-
-  if (iter_original == get_module_map().end())
-    {
-      printf("ERROR: alias %s could not be added: operator %s does not exist\n", alias.c_str(), original.c_str());
-      return -2;
-    }
-
-  if (get_module_map().find(alias) != get_module_map().end())
-    {
-      printf("ERROR alias %s could not be added: alias name already exists as an operator\n", alias.c_str());
-      return -3;
-    }
-
-  get_aliases()[alias] = original;
-
-  return 0;
-}
-
-/***
- * returns the module of given operator
- *
- * parameters:
- *      std::string operatorName -> name of the operator
- * return value:
- *      std::string -> name of the operators module
- */
-std::string
-get_module_name_to(const std::string &operatorName)
-{
-  // if not true the programm will exit in function check_operator
-  if (check_operator(operatorName))
-    {
-      if (get_module_map().count(operatorName) > 0) { return get_module_map()[operatorName]; }
-      else if (get_aliases().count(operatorName) > 0) { return get_module_map()[get_aliases()[operatorName]]; }
-    }
-  // Only to quell the warning. Function should never reach this.
-  return "";
-}
-
-/**
- * @fn void *(*operatorModule(const char *operatorName))(void *)
-
- * returns the function of the module of \a operatorName
- * @param operatorName name of the operator
- * @returns the function of the #module_t of \a operatorName
- */
-/* not used
-void *(*operatorModule(const std::string &operatorName))(void *)
-{
-  std::string operator_name = std::string(operatorName);
-  return get_modules()[get_module_name_to(operatorName)].func;
-}
-*/
-
-/***
- * returns help for given operator name
- * if there is no help nullptr will be returned
- * @param operatorName operator name
- * @return vector of strings containing the help
- */
-const char **
-operator_help(const std::string &operatorName)
-{
-  return get_modules()[get_module_name_to(get_original(operatorName))].help;
-}
-
-/***
- * Returns the number type this operator works with
- * @param operatorName operator name
- * @reval short
- */
-int
-operator_stream_number(std::string &p_operatorName)
-{
-  return get_modules()[get_module_name_to(p_operatorName)].get_number();
-}
-
-/***
- * Creates a sorted vector with all operator names and alisases excluding all modules that are marked as internal
- * @return a sorted std::vector containing all operator names and aliases
- * excluding all operators which modules are marked as internal
- */
-std::vector<std::string>
-get_sorted_operator_name_list()
-{
-  std::vector<std::string> names;
-  for (const auto &operator_module_names_pair : get_module_map())
-    {
-      if (get_modules()[operator_module_names_pair.second].get_mode() == 1) { names.push_back(operator_module_names_pair.first); }
-    }
-  // adding operators names from alias_map
-  for (const auto &alias : get_aliases()) { names.push_back(alias.first); }
-
-  std::sort(names.begin(), names.end());
-
-  return names;
-}
-
-std::vector<std::string>
-get_no_output_operator_list()
-{
-  std::vector<std::string> names;
-  for (const auto &operator_module_names_pair : get_module_map())
-    {
-      if (get_modules()[operator_module_names_pair.second].get_mode() == 1
-          && get_modules()[operator_module_names_pair.second].get_stream_out_cnt() == 0)
-        {
-          names.push_back(operator_module_names_pair.first);
-        }
-    }
-  // adding operators names from alias_map
-  for (const auto &alias : get_aliases())
-    {
-      const auto &original = alias.second;
-      if (get_modules()[get_module_map()[original]].get_mode() == 1
-          && get_modules()[get_module_map()[original]].get_stream_out_cnt() == 0)
-        {
-          names.push_back(alias.first);
-        }
-    }
-
-  std::sort(names.begin(), names.end());
-
-  return names;
-}
-
-void
-operatorPrintAll(void)
-{
-  int number_of_chars = 0;
-  std::string tab = "   ";
-  int tab_width = tab.size();
-  // using a set because it sorts the operators alphabetically on its own
-  std::vector<std::string> sorted_operator_names = get_sorted_operator_name_list();
-
-  std::cout << tab;
-  for (const auto &operatorName : sorted_operator_names)
-    {
-      if (number_of_chars > 85)
-        {
-          number_of_chars = tab_width;
-          std::cerr << std::endl << tab;
-        }
-
-      std::cerr << " " << operatorName;
-      number_of_chars += 1 + operatorName.size();
-    }
-
-  std::cerr << std::endl;
-}
-
-// helper function for setting the spacing in operator_print_list
-static std::string
-get_spacing_for(int p_space, const std::string &str)
-{
-  std::string spacing = "";
-  for (int i = str.size(); i <= p_space; ++i) spacing += " ";
-  return spacing;
-}
-
-static std::string
-get_operator_description(const std::string &p_current_op_name, const char **help)
-{
-  std::string description = "";
-  unsigned long cur_help_idx = 0;
-  std::string line;
-  unsigned long operator_section = 0;
-
-  // search for operator section
-  size_t help_size = 0;
-  while (help[help_size]) help_size++;
-  if (!help_size) return description;
-  while (operator_section == 0 && cur_help_idx < help_size - 1)
-    {
-      line = help[++cur_help_idx];
-      if (line.find("OPERATORS") != std::string::npos) { operator_section = cur_help_idx; }
-    }
-  // if no operator section is found
-  if (operator_section == 0)
-    {
-      cur_help_idx = 0;
-      line = help[0];
-      std::string name_section = help[0];
-      bool help_contains_name = false;
-      // search for the operator name in the description
-      while (!line.empty())
-        {
-          line = help[++cur_help_idx];
-          if (line.find(p_current_op_name) != std::string::npos) { help_contains_name = true; }
-          name_section += line;
-        }
-      // if the name was found save description for later use
-      if (help_contains_name) { description = name_section.substr(name_section.find('-') + 2, name_section.size()); }
-    }
-  else
-    {
-      line = help[++operator_section];
-      // search the operator section for current operator line
-      while (line.find(p_current_op_name + " ") == std::string::npos && !line.empty() && operator_section < help_size - 1)
-        {
-          line = help[++operator_section];
-        }
-      // if operator line found save description for later use
-      if (!line.empty() && line.find("    " + p_current_op_name + " ") != std::string::npos)
-        {
-          auto op_name_start = line.find_first_not_of(" \t");
-
-          description = line.substr(line.find_first_not_of(" \t", op_name_start + p_current_op_name.size()), line.size());
-        }
-    }
-
-  return description;
-}
-
-/***
- * Prints all operator names and their short descriptions
- * Aliases are listed and point to their original operator name.
- * If the module is not documented the description is empty
- * If a module has only one operator the short module description is listed
- * If the operator is not documented the description is empty
- */
-void
-operator_print_list(bool print_no_output)
-{
-  std::vector<std::string> output_list = print_no_output ? get_no_output_operator_list() : get_sorted_operator_name_list();
-
-  auto list_length = output_list.size();
-
-  // help variables
-
-  for (size_t out_list_idx = 0; out_list_idx < list_length; out_list_idx++)
-    {
-      auto &current_op_name = output_list[out_list_idx];
-      const auto *current_module = &get_modules()[get_module_name_to(current_op_name)];
-      if (get_aliases().find(current_op_name) != get_aliases().end())
-        {
-          output_list[out_list_idx] += get_spacing_for(16, current_op_name) + "--> " + get_aliases()[current_op_name];
-        }
-      else if (current_module->help)
-        {
-          // add spaceing and saving output line to the output list
-          auto description = get_operator_description(current_op_name, current_module->help);
-          output_list[out_list_idx] += get_spacing_for(16, current_op_name) + description;
-        }
-      std::string in_out_info = " (" + std::to_string(current_module->get_stream_in_cnt()) + "|"
-                                + std::to_string(current_module->get_stream_out_cnt()) + ")";
-      output_list[out_list_idx] += get_spacing_for(90, output_list[out_list_idx]) + in_out_info;
-    }
-  // print generated output list
-  for (const std::string &str : output_list) { std::cout << str << std::endl; }
-}
-
-std::map<std::string, module_t>::iterator
-find_module(const std::string &operator_name)
-{
-  std::string operName = operator_name;
-
-  auto it = get_aliases().find(operName);
-  if (it != get_aliases().end()) { operName = it->second; }
-
-  auto modMapIter = get_module_map().find(operName);
-  auto modIter = get_modules().end();
-  if (!(modMapIter == get_module_map().end())) { modIter = get_modules().find(modMapIter->second); }
-  return modIter;
-}
-
-std::vector<std::string>
-get_module_operator_names(std::string module_name)
-{
-  auto &modules = get_modules();
-  if (std::islower(static_cast<unsigned char>(module_name[0]))) {module_name[0] = std::toupper(module_name[0]); }
-  if (modules.find(module_name) == modules.end()) { return std::vector<std::string>(); }
-  return modules[module_name].operators;
-}
-
-module_t &
-get_module(const std::string &operator_name)
-{
-  return get_modules()[get_module_name_to(get_original(operator_name))];
-}
diff --git a/src/modules.h b/src/modules.h
index 29a88ee6b28562988ba462f2cdcd2e236ddc061d..e1c425e44298e81a552ceacfac37483334e0e746 100644
--- a/src/modules.h
+++ b/src/modules.h
@@ -12,104 +12,19 @@
 #include <array>
 #include <vector>
 
-#include "module_static_maps.h"
-
-// Obase uses input name as base name for files e.g 'test' gets used as test_001 test_002 which are created inside the operator
-#define OBASE -1
-#define INTERNAL 0
-#define EXPOSED 1
+#include <functional>
+#include <memory>
+#include "cdo_module.h"
+#include "process.h"
 
 /***
   type definition for module functions loaded from a custom module
   */
 using dyn_oper_t = void (*)(void *arg);
 
-enum PositionRestrictions
-{
-  NoRestriction = 0,
-  FilesOnly = 1,
-  OnlyFirst = 2
-};
-
-struct Alias
-{
-  Alias(const std::string &_alias, const std::string &_original);
-  std::string alias;
-  std::string original;
-};
-
-struct module_constraints
-{
-  short streamInCnt;   // Number of input streams
-  short streamOutCnt;  // Number of output streams
-  PositionRestrictions pos_restriction = NoRestriction;
-};
-
-struct module_t
-{
-  std::string mod_name;
-  void *(*func)(void *);               // Module
-  const char **help;                   // Help
-  short mode;                          // Module mode: 0:intern 1:extern
-  short number;                        // Allowed number type
-  std::vector<std::string> operators;  // Operator names
-  module_constraints constraints;
-  std::vector<Alias> aliases;
-
-  module_t(const std::string &mod_name, void *(*p_func)(void *), const char **p_help, const std::vector<std::string> &p_opers,
-           short p_m, short p_n, short p_siC, short p_soC, PositionRestrictions p_pos_restriction,
-           const std::vector<Alias> &p_aliases = {});
-  module_t(){};
-  std::string toString();
-  int
-  get_stream_in_cnt() const
-  {
-    return constraints.streamInCnt;
-  }
-  int
-  get_stream_out_cnt() const
-  {
-    return constraints.streamOutCnt;
-  }
-  int
-  get_number() const
-  {
-    return number;
-  }
-  int
-  get_mode() const
-  {
-    return mode;
-  }
-  int
-  get_pos_restriction()
-  {
-    return constraints.pos_restriction;
-  }
-};
-
 /***
   vector for library handles for loaded custom modules
   */
 extern std::vector<void *> custom_modules_lib_handles;
 
-std::string extract_operator_name(const std::string &operatorCommand);
-void extract_name_and_argument(const std::string &command, std::string &operatorName, std::string &operatorArgument);
-
-std::string find_similar_operators(const std::string &operatorName);
-
-std::map<std::string, module_t>::iterator find_module(const std::string &operatorName);
-std::vector<std::string> get_module_operator_names(std::string module_name);
-module_t &get_module(const std::string &operatorName);
-
-std::string get_original(const std::string &operatorName);
-
-void init_modules();
-
-const char **operator_help(const std::string &operatorName);
-int operator_stream_number(std::string &operatorName);
-
-std::string get_module_name_to(const std::string &operatorName);
-std::vector<std::string> get_sorted_operator_name_list();
-
 #endif /* MODULES_H */
diff --git a/src/mpim_grid/grid_healpix.cc b/src/mpim_grid/grid_healpix.cc
index c11ab7de2f9978b12d531ef239edb17873bc15b6..9c2fd62d198fde141cd925aece791569db9347f1 100644
--- a/src/mpim_grid/grid_healpix.cc
+++ b/src/mpim_grid/grid_healpix.cc
@@ -160,7 +160,7 @@ hp_generate_ring_indices(HpOrder order, int nside, size_t gridsize, std::vector<
   // clang-format on
 
   int numRows = 4 * nside - 1;
-  if (ringRows.size() < (size_t)numRows) ringRows.resize(numRows);
+  if (ringRows.size() < (size_t) numRows) ringRows.resize(numRows);
   if (order != HpOrder::Ring && ringIndices.size() < gridsize) ringIndices.resize(gridsize);
 
   int index = 0;
@@ -183,7 +183,8 @@ hp_generate_ring_indices(HpOrder order, int nside, size_t gridsize, std::vector<
 }
 
 template <typename T>
-void hp_ring_to_nested(int nside, size_t gridsize, T *arrayIn, T *arrayOut)
+void
+hp_ring_to_nested(int nside, size_t gridsize, T *arrayIn, T *arrayOut)
 {
   assert(gridsize <= INT_MAX && "Large grid size unsupported!");
 
@@ -198,9 +199,9 @@ void hp_ring_to_nested(int nside, size_t gridsize, T *arrayIn, T *arrayOut)
 template void hp_ring_to_nested(int nside, size_t gridsize, float *arrayIn, float *arrayOut);
 template void hp_ring_to_nested(int nside, size_t gridsize, double *arrayIn, double *arrayOut);
 
-
 template <typename T>
-void hp_nested_to_ring(int nside, size_t gridsize, T *arrayIn, T *arrayOut)
+void
+hp_nested_to_ring(int nside, size_t gridsize, T *arrayIn, T *arrayOut)
 {
   assert(gridsize <= INT_MAX && "Large grid size unsupported!");
 
diff --git a/src/mpim_grid/grid_proj.cc b/src/mpim_grid/grid_proj.cc
index be8d23f5c3a33bedd6cc0ee1612eaf03eb8fddf6..54ecd3c8ae31dd23d647b6f9de3941e44d52969b 100644
--- a/src/mpim_grid/grid_proj.cc
+++ b/src/mpim_grid/grid_proj.cc
@@ -53,12 +53,13 @@ check_xyvals(size_t nvals, double *xvals, double *yvals)
 static std::string
 gen_param(const char *fmt, ...)
 {
-  va_list args;
-  char str[256];
+  constexpr size_t len = 256;
+  char str[len];
 
+  va_list args;
   va_start(args, fmt);
 
-  vsprintf(str, fmt, args);
+  vsnprintf(str, len, fmt, args);
 
   va_end(args);
 
@@ -610,22 +611,6 @@ cdo_proj_to_lonlat(char *proj_params, size_t nvals, double *xvals, double *yvals
 double
 gridGetPlanetRadius(int gridID)
 {
-  double planetRadius = 0.0;
-
-  auto gridtype = gridInqType(gridID);
-  auto projtype = (gridtype == GRID_PROJECTION) ? gridInqProjType(gridID) : -1;
-  if (projtype == CDI_PROJ_LCC)
-    {
-      CDI_GridProjParams gpp;
-      gridInqParamsLCC(gridID, &gpp);
-      if (IS_EQUAL(gpp.b, gpp.mv)) planetRadius = gpp.a;
-    }
-  else if (projtype == CDI_PROJ_STERE)
-    {
-      CDI_GridProjParams gpp;
-      gridInqParamsSTERE(gridID, &gpp);
-      if (IS_EQUAL(gpp.b, gpp.mv)) planetRadius = gpp.a;
-    }
-
-  return planetRadius;
+  double radius{ 0 };
+  return (CDI_NOERR == cdiInqAttFlt(gridID, CDI_GLOBAL, "earth_radius", 1, &radius) && radius > 1) ? radius : 0.0;
 }
diff --git a/src/mpim_grid/grid_rot.cc b/src/mpim_grid/grid_rot.cc
index c3b2c55fe352e6bfebf826ca4b2477d3289d7b4d..361ad67630b0265c0a9003710c0cdde244b1b7ef 100644
--- a/src/mpim_grid/grid_rot.cc
+++ b/src/mpim_grid/grid_rot.cc
@@ -38,6 +38,7 @@ lamrot_to_lam(double phirot, double lamrot, double polphi, double pollam, double
   if (lamrot > 180.0) lamrot -= 360.0;
   double zlamrot = DEG2RAD * lamrot;
 
+  if (polgam < 0) polgam += 360.0;
   if (polgam > 0)
     {
       double zgam = DEG2RAD * polgam;
@@ -95,6 +96,7 @@ phirot_to_phi(double phirot, double lamrot, double polphi, double polgam)
   if (lamrot > 180.0) lamrot -= 360.0;
   double zlamrot = DEG2RAD * lamrot;
 
+  if (polgam < 0) polgam += 360.0;
   if (polgam > 0)
     {
       double zgam = DEG2RAD * polgam;
@@ -131,7 +133,7 @@ lam_to_lamrot(double phi, double rla, double polphi, double pollam)
   double zrla = DEG2RAD * rla;
   double zphi = DEG2RAD * phi;
 
-  double zarg1 = -sin(zrla - zlampol) * std::cos(zphi);
+  double zarg1 = -std::sin(zrla - zlampol) * std::cos(zphi);
   double zarg2 = -zsinpol * std::cos(zphi) * std::cos(zrla - zlampol) + zcospol * std::sin(zphi);
 
   if (std::fabs(zarg2) < 1.0e-20) zarg2 = 1.0e-20;
@@ -200,7 +202,7 @@ usvs_to_uv(double us, double vs, double phi, double rla, double polphi, double p
   double zrlas = lam_to_lamrot(phi, rla, polphi, pollam) * DEG2RAD;
 
   // winkel zbeta berechen (schnittwinkel der breitenkreise)
-  double zarg = -sin(zpolphi) * std::sin(zrla - zpollam) * std::sin(zrlas) - std::cos(zrla - zpollam) * std::cos(zrlas);
+  double zarg = -std::sin(zpolphi) * std::sin(zrla - zpollam) * std::sin(zrlas) - std::cos(zrla - zpollam) * std::cos(zrlas);
   if (zarg > 1.0) zarg = 1.0;
   if (zarg < -1.0) zarg = -1.0;
   /*
diff --git a/src/mpim_grid/mpim_grid.cc b/src/mpim_grid/mpim_grid.cc
index 8322a63bf05feddd6d9d98280db6251d704aab63..4c8f082885897b759ba3ba0ab4724915aa814de6 100644
--- a/src/mpim_grid/mpim_grid.cc
+++ b/src/mpim_grid/mpim_grid.cc
@@ -208,10 +208,7 @@ gridToZonal(int gridID1)
       gridDefXvals(gridID2, &xval);
       gridDefYvals(gridID2, latitudes.data());
     }
-  else
-    {
-      cdo_abort("Gridtype %s unsupported!", gridNamePtr(gridtype));
-    }
+  else { cdo_abort("Gridtype %s unsupported!", gridNamePtr(gridtype)); }
 
   return gridID2;
 }
@@ -238,10 +235,7 @@ gridToMeridional(int gridID1)
       double yval = 0.0;
       gridDefYvals(gridID2, &yval);
     }
-  else
-    {
-      cdo_abort("Gridtype %s unsupported!", gridNamePtr(gridtype));
-    }
+  else { cdo_abort("Gridtype %s unsupported!", gridNamePtr(gridtype)); }
 
   return gridID2;
 }
@@ -630,7 +624,7 @@ reduced_grid_is_global(int np, int nxmax, double xfirst, double xlast)
 }
 
 void
-field2regular(int gridID1, int gridID2, double missval, double *array, size_t nmiss, int lnearest)
+field2regular(int gridID1, int gridID2, double missval, double *array, size_t numMissVals, int lnearest)
 {
   auto gridtype = gridInqType(gridID1);
   if (gridtype != GRID_GAUSSIAN_REDUCED) cdo_abort("Not a reduced Gaussian grid!");
@@ -648,7 +642,7 @@ field2regular(int gridID1, int gridID2, double missval, double *array, size_t nm
   int nxmax = 0;
   for (size_t i = 0; i < ny; ++i) nxmax = std::max(nxmax, reducedPoints[i]);
 
-  int lmiss = (nmiss > 0);
+  int lmiss = (numMissVals > 0);
   int lperio = 1;
 
   int iret;
@@ -1241,10 +1235,7 @@ gridToUnstructuredGaussianReduced(int gridID1, int gridID2, size_t gridsize, int
 
       gridDefYvals(gridID2, yvals2D.data());
     }
-  else
-    {
-      cdo_abort("%s: latitude coordinates missing!", gridNamePtr(gridInqType(gridID1)));
-    }
+  else { cdo_abort("%s: latitude coordinates missing!", gridNamePtr(gridInqType(gridID1))); }
 
   std::vector<double> xvals(gridsize);
 
@@ -1392,14 +1383,8 @@ gridToUnstructured(int gridID1, NeedCorners needCorners)
           gridtype = GRID_LONLAT;
           isProjRLL = true;
         }
-      else if (projType == CDI_PROJ_HEALPIX)
-        {
-          isProjHealpix = true;
-        }
-      else
-        {
-          cdo_abort("Projection unsupported!");
-        }
+      else if (projType == CDI_PROJ_HEALPIX) { isProjHealpix = true; }
+      else { cdo_abort("Projection unsupported!"); }
     }
 
   switch (gridtype)
@@ -1520,6 +1505,36 @@ gridCurvilinearToRegular(int gridID1)
   return gridID2;
 }
 
+int
+gridProjectionToRegular(int gridID1)
+{
+  int gridID2 = -1;
+
+  auto gridtype = gridInqType(gridID1);
+  auto xunits = cdo::inq_key_string(gridID1, CDI_XAXIS, CDI_KEY_UNITS);
+  auto yunits = cdo::inq_key_string(gridID1, CDI_YAXIS, CDI_KEY_UNITS);
+
+  if (gridtype == GRID_PROJECTION && xunits.rfind("deg", 0) == 0 && yunits.rfind("deg", 0) == 0)
+    {
+      auto gridsize = gridInqSize(gridID1);
+      auto nx = gridInqXsize(gridID1);
+      auto ny = gridInqYsize(gridID1);
+
+      std::vector<double> xvals(nx), yvals(ny);
+      gridInqXvals(gridID1, xvals.data());
+      gridInqYvals(gridID1, yvals.data());
+
+      gridID2 = gridCreate(GRID_LONLAT, gridsize);
+      gridDefXsize(gridID2, nx);
+      gridDefYsize(gridID2, ny);
+
+      gridDefXvals(gridID2, xvals.data());
+      gridDefYvals(gridID2, yvals.data());
+    }
+
+  return gridID2;
+}
+
 static int
 compute_gridcell_weights(int gridID, const Varray<double> &gridCellArea, Varray<double> &gridCellWeights)
 {
@@ -1587,10 +1602,7 @@ gridcell_weights(int gridID, Varray<double> &gridCellWeights)
           if (areaStatus != 0 && (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN))
             areaStatus = gridGenAreaReg2Dweights(gridID, gridCellArea.data());
         }
-      else
-        {
-          areaStatus = 1;
-        }
+      else { areaStatus = 1; }
     }
 
   if (areaStatus == 0) { weightStatus = compute_gridcell_weights(gridID, gridCellArea, gridCellWeights); }
@@ -1735,7 +1747,7 @@ LonLatUnits
 string_to_LonLatUnits(const std::string &units, const std::string &description)
 {
   if (units.rfind("rad", 0) == 0) { return LonLatUnits::Rad; }
-  else if (units.rfind("deg", 0) == 0)  { return LonLatUnits::Deg; }
+  else if (units.rfind("deg", 0) == 0) { return LonLatUnits::Deg; }
 
   static bool warn = true;
   if (warn)
@@ -1751,5 +1763,4 @@ LonLatUnits
 cdo_grid_get_units(int gridID, int varID, const std::string &description)
 {
   return string_to_LonLatUnits(cdo::inq_key_string(gridID, varID, CDI_KEY_UNITS), description);
-;
 }
diff --git a/src/mpim_grid/mpim_grid.h b/src/mpim_grid/mpim_grid.h
index 3951ed2aa8b8e01f00ca55cf5dbd8d2b5eab1ccb..2539bd0b6ed28cae7d64c06aef329033f86f9a06 100644
--- a/src/mpim_grid/mpim_grid.h
+++ b/src/mpim_grid/mpim_grid.h
@@ -20,7 +20,6 @@
 #include "grid_convert.h"
 #include "varray.h"
 
-
 enum class LonLatUnits
 {
   Deg,
@@ -106,8 +105,9 @@ int gridToUnstructured(int gridID, NeedCorners needCorners = NeedCorners::No);
 int gridToUnstructuredSelecton(int gridID1, const std::vector<size_t> &selectionIndexList, int nocoords, int nobounds);
 int gridToCurvilinear(int gridID, NeedCorners needCorners = NeedCorners::No);
 int gridCurvilinearToRegular(int gridID);
+int gridProjectionToRegular(int gridID);
 int gridToRegular(int gridID);
-void field2regular(int gridID1, int gridID2, double missval, double *array, size_t nmiss, int lnearest);
+void field2regular(int gridID1, int gridID2, double missval, double *array, size_t numMissVals, int lnearest);
 
 // GME grid
 void gme_factorni(int kni, int *kni2, int *kni3);
diff --git a/src/namelist.cc b/src/namelist.cc
index 8f6caa21ade00e9b3a3451e0adbbcc284d353f9b..979dad95a34ddf70dd1352a4f6670648d8518162 100644
--- a/src/namelist.cc
+++ b/src/namelist.cc
@@ -148,11 +148,11 @@ NamelistParser::checkKeyname(const char *buf, NamelistToken *t)
   switch (t->type)
     {
     case NamelistType::STRING:
-      while (isspace((int) buf[t->start]) && t->start < t->end) t->start++;
-      while (isspace((int) buf[t->end - 1]) && t->start < t->end) t->end--;
+      while (std::isspace((int) buf[t->start]) && t->start < t->end) t->start++;
+      while (std::isspace((int) buf[t->end - 1]) && t->start < t->end) t->end--;
       if ((t->end - t->start) < 1) return NamelistError::EMKEY;
       for (long i = t->start; i < t->end; ++i)
-        if (isspace((int) buf[i])) return NamelistError::INKEY;
+        if (std::isspace((int) buf[i])) return NamelistError::INKEY;
       t->type = NamelistType::KEY;
       break;
     case NamelistType::WORD: t->type = NamelistType::KEY; break;
diff --git a/src/node.cc b/src/node.cc
index f57619f9fa1453d8b0eef04730d70174f18c68c7..8873b7ffe20b2aa9723424abaabc75cdc6d2a756 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -38,8 +38,8 @@ Node::is_done()
 {
   bool done = false;
 
-  if (constraints.streamInCnt == -1) { done = false; } // varibale inputs always false
-  else if (isFile && !isOutFile) { done = true; } // files always true
+  if (constraints.streamInCnt == -1) { done = false; }  // varibale inputs always false
+  else if (isFile && !isOutFile) { done = true; }       // files always true
   else if ((int) children.size() == constraints.streamInCnt) { done = true; }
   Debug(CDO_NODE, "%s is done: %s", oper, done ? "true" : "false");
 
diff --git a/src/node.h b/src/node.h
index c62b66772faf2231860909f449fd7163cabe1853..e3b49aab1d2f26d7b52624a3647dc499ec399a64 100644
--- a/src/node.h
+++ b/src/node.h
@@ -5,11 +5,11 @@
 #include <string>
 #include <memory>
 #include <iostream>
-#include "modules.h"
+#include "cdo_module.h"
 
 static std::string errmsg_node_to_many_inputs = "To many inputs";
-static std::string errmsg_node_no_output= "Operator has no output, cannot be used with pipes unless used first";
-static std::string errmsg_node_unassigned  ="Could not be assigned, leftover input";
+static std::string errmsg_node_no_output = "Operator has no output, cannot be used with pipes unless used first";
+static std::string errmsg_node_unassigned = "Could not be assigned, leftover input";
 static std::string errmsg_node_file_to_file = "Attempted to attach file to file";
 static std::string errmsg_node_only_accepts_files = "Operator cannot be piped into an operator that takes only files";
 
@@ -60,5 +60,4 @@ public:
   std::string to_string();
 };
 
-
 #endif
diff --git a/src/operator_help.cc b/src/operator_help.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ef680ef478e4e58058f44ab6e0768d8d31b97b5b
--- /dev/null
+++ b/src/operator_help.cc
@@ -0,0 +1,7105 @@
+// Automatically created with makedoc, don't edit!
+
+#include "operator_help.h"
+
+// clang-format off
+
+const CdoHelp InfoHelp = {
+    "NAME",
+    "    info, infon, map - Information and simple statistics",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infiles",
+    "",
+    "DESCRIPTION",
+    "    This module writes information about the structure and contents for each field of all input files",
+    "    to standard output. A field is a horizontal layer of a data variable. All input files need to have ",
+    "    the same structure with the same variables on different timesteps.",
+    "    The information displayed depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    info   Dataset information listed by parameter identifier",
+    "           Prints information and simple statistics for each field of all input datasets.",
+    "           For each field the operator prints one line with the following elements:",
+    "           - Date and Time",
+    "           - Level, Gridsize and number of Missing values",
+    "           - Minimum, Mean and Maximum \\",
+    "           The mean value is computed without the use of area weights!",
+    "           - Parameter identifier",
+    "    infon  Dataset information listed by parameter name",
+    "           The same as operator info but using the name instead of the",
+    "           identifier to label the parameter.",
+    "    map    Dataset information and simple map",
+    "           Prints information, simple statistics and a map for each field of all input",
+    "           datasets. The map will be printed only for fields on a regular lon/lat grid.",
+};
+
+const CdoHelp SinfoHelp = {
+    "NAME",
+    "    sinfo, sinfon - Short information",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infiles",
+    "",
+    "DESCRIPTION",
+    "    This module writes information about the structure of infiles to standard output.",
+    "    infiles is an arbitrary number of input files. All input files need to have ",
+    "    the same structure with the same variables on different timesteps.",
+    "    The information displayed depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    sinfo   Short information listed by parameter identifier",
+    "            Prints short information of a dataset. The information is divided into 4 sections.",
+    "            Section 1 prints one line per parameter with the following information:",
+    "            - institute and source",
+    "            - time c=constant v=varying",
+    "            - type of statistical processing",
+    "            - number of levels and z-axis number",
+    "            - horizontal grid size and number",
+    "            - data type",
+    "            - parameter identifier",
+    "            Section 2 and 3 gives a short overview of all grid and vertical coordinates.",
+    "            And the last section contains short information of the time coordinate.",
+    "    sinfon  Short information listed by parameter name",
+    "            The same as operator sinfo but using the name instead of the identifier to label the parameter.",
+};
+
+const CdoHelp XSinfoHelp = {
+    "NAME",
+    "    xsinfo, xsinfop - Extra short information",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infiles",
+    "",
+    "DESCRIPTION",
+    "    This module writes information about the structure of infiles to standard output.",
+    "    infiles is an arbitrary number of input files. All input files need to have ",
+    "    the same structure with the same variables on different timesteps.",
+    "    The information displayed depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    xsinfo   Extra short information listed by parameter name",
+    "             Prints short information of a dataset. The information is divided into 4 sections.",
+    "             Section 1 prints one line per parameter with the following information:",
+    "             - institute and source",
+    "             - time c=constant v=varying",
+    "             - type of statistical processing",
+    "             - number of levels and z-axis number",
+    "             - horizontal grid size and number",
+    "             - data type",
+    "             - memory type (float or double)",
+    "             - parameter name",
+    "             Section 2 to 4 gives a short overview of all grid, vertical and time coordinates.",
+    "    xsinfop  Extra short information listed by parameter identifier",
+    "             The same as operator xsinfo but using the identifier instead of the name to label the parameter.",
+};
+
+const CdoHelp DiffHelp = {
+    "NAME",
+    "    diff, diffn - Compare two datasets field by field",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,options]  infile1 infile2",
+    "",
+    "DESCRIPTION",
+    "    Compares the contents of two datasets field by field. The input datasets need",
+    "    to have the same structure and its fields need to have the dimensions.",
+    "    Try the option names if the number of variables differ.",
+    "    Exit status is 0 if inputs are the same and 1 if they differ.",
+    "",
+    "OPERATORS",
+    "    diff   Compare two datasets listed by parameter id",
+    "           Provides statistics on differences between two datasets.",
+    "           For each pair of fields the operator prints one line with the following information:",
+    "           - Date and Time",
+    "           - Level, Gridsize and number of Missing values",
+    "           - Number of different values",
+    "           - Occurrence of coefficient pairs with different signs (S)",
+    "           - Occurrence of zero values (Z)",
+    "           - Maxima of absolute difference of coefficient pairs",
+    "           - Maxima of relative difference of non-zero coefficient pairs with equal signs",
+    "           - Parameter identifier",
+    "    diffn  Compare two datasets listed by parameter name",
+    "           The same as operator diff. Using the name instead of the",
+    "           identifier to label the parameter.",
+    "",
+    "PARAMETER",
+    "    maxcount  INTEGER Stop after maxcount different fields",
+    "    abslim    FLOAT   Limit of the maximum absolute difference (default: 0)",
+    "    rellim    FLOAT   Limit of the maximum relative difference (default: 1)",
+    "    names     STRING  Consideration of the variable names of only one input file (left/right) or the intersection of both (intersect).",
+    "              ",
+};
+
+const CdoHelp NinfoHelp = {
+    "NAME",
+    "    npar, nlevel, nyear, nmon, ndate, ntime, ngridpoints, ngrids - ",
+    "    Print the number of parameters, levels or times",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile",
+    "",
+    "DESCRIPTION",
+    "    This module prints the number of variables, levels or times of the ",
+    "    input dataset.",
+    "",
+    "OPERATORS",
+    "    npar         Number of parameters",
+    "                 Prints the number of parameters (variables).",
+    "    nlevel       Number of levels",
+    "                 Prints the number of levels for each variable.",
+    "    nyear        Number of years",
+    "                 Prints the number of different years.",
+    "    nmon         Number of months",
+    "                 Prints the number of different combinations of years and months.",
+    "    ndate        Number of dates",
+    "                 Prints the number of different dates.",
+    "    ntime        Number of timesteps",
+    "                 Prints the number of timesteps.",
+    "    ngridpoints  Number of gridpoints",
+    "                 Prints the number of gridpoints for each variable.",
+    "    ngrids       Number of horizontal grids",
+    "                 Prints the number of horizontal grids.",
+};
+
+const CdoHelp ShowinfoHelp = {
+    "NAME",
+    "    showformat, showcode, showname, showstdname, showlevel, showltype, showyear, ",
+    "    showmon, showdate, showtime, showtimestamp - Show variables, levels or times",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile",
+    "",
+    "DESCRIPTION",
+    "    This module prints the format, variables, levels or times of the input dataset.",
+    "",
+    "OPERATORS",
+    "    showformat     Show file format",
+    "                   Prints the file format of the input dataset.",
+    "    showcode       Show code numbers",
+    "                   Prints the code number of all variables.",
+    "    showname       Show variable names",
+    "                   Prints the name of all variables.",
+    "    showstdname    Show standard names",
+    "                   Prints the standard name of all variables.",
+    "    showlevel      Show levels",
+    "                   Prints all levels for each variable.",
+    "    showltype      Show GRIB level types",
+    "                   Prints the GRIB level type for all z-axes.",
+    "    showyear       Show years",
+    "                   Prints all years.",
+    "    showmon        Show months",
+    "                   Prints all months.",
+    "    showdate       Show date information",
+    "                   Prints date information of all timesteps (format YYYY-MM-DD).",
+    "    showtime       Show time information",
+    "                   Prints time information of all timesteps (format hh:mm:ss).",
+    "    showtimestamp  Show timestamp",
+    "                   Prints timestamp of all timesteps (format YYYY-MM-DDThh:mm:ss).",
+};
+
+const CdoHelp ShowattributeHelp = {
+    "NAME",
+    "    showattribute - Show attributes",
+    "",
+    "SYNOPSIS",
+    "    showattribute[,attributes]  infile",
+    "",
+    "DESCRIPTION",
+    "    This operator prints the attributes of the data variables of a dataset.",
+    "    ",
+    "    Each attribute has the following structure:",
+    "    ",
+    "      [var_nm@][att_nm]",
+    "    ",
+    "       var_nm  Variable name (optional). Example: pressure",
+    "       att_nm  Attribute name (optional). Example: units",
+    "    ",
+    "    The value of var_nm is the name of the variable containing the attribute (named att_nm) that",
+    "    you want to print. Use wildcards to print the attribute att_nm of more than one variable.",
+    "    A value of var_nm of '*' will print the attribute att_nm of all data variables.",
+    "    If var_nm is missing then att_nm refers to a global attribute.",
+    "    ",
+    "    The value of att_nm is the name of the attribute you want to print. Use wildcards to print more than",
+    "    one attribute. A value of att_nm of '*' will print all attributes.",
+    "",
+    "PARAMETER",
+    "    attributes  STRING  Comma-separated list of attributes. ",
+};
+
+const CdoHelp FiledesHelp = {
+    "NAME",
+    "    partab, codetab, griddes, zaxisdes, vct - Dataset description",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile",
+    "",
+    "DESCRIPTION",
+    "    This module provides operators to print meta information about a dataset.",
+    "    The printed meta-data depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    partab    Parameter table",
+    "              Prints all available meta information of the variables.",
+    "    codetab   Parameter code table",
+    "              Prints a code table with a description of all variables.",
+    "              For each variable the operator prints one line listing the",
+    "              code, name, description and units.",
+    "    griddes   Grid description",
+    "              Prints the description of all grids.",
+    "    zaxisdes  Z-axis description",
+    "              Prints the description of all z-axes.",
+    "    vct       Vertical coordinate table",
+    "              Prints the vertical coordinate table.",
+};
+
+const CdoHelp ApplyHelp = {
+    "NAME",
+    "    apply - Apply operators",
+    "",
+    "SYNOPSIS",
+    "    apply,operators  infiles",
+    "",
+    "DESCRIPTION",
+    "    The apply utility runs the named operators on each input file. The input files must be enclosed in square brackets.",
+    "    This utility can only be used on a series of input files. These are all operators with more than one input file (infiles).",
+    "    Here is an incomplete list of these operators: copy, cat, merge, mergetime, select, ENSSTAT.",
+    "    The parameter operators is a blank-separated list of CDO operators. Use quotation marks if more than one operator is needed.",
+    "    Each operator may have only one input and output stream.",
+    "",
+    "PARAMETER",
+    "    operators  STRING    Blank-separated list of CDO operators.",
+};
+
+const CdoHelp CopyHelp = {
+    "NAME",
+    "    copy, clone, cat - Copy datasets",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators to copy, clone or concatenate datasets.",
+    "    infiles is an arbitrary number of input files. All input files need to have ",
+    "    the same structure with the same variables on different timesteps.",
+    "",
+    "OPERATORS",
+    "    copy   Copy datasets",
+    "           Copies all input datasets to outfile. ",
+    "    clone  Clone datasets",
+    "           Copies all input datasets to outfile. In contrast to the copy operator, clone tries",
+    "           not to change the input data. GRIB records are neither decoded nor decompressed.",
+    "    cat    Concatenate datasets",
+    "           Concatenates all input datasets and appends the result to the end ",
+    "           of outfile. If outfile does not exist it will be created.",
+};
+
+const CdoHelp TeeHelp = {
+    "NAME",
+    "    tee - Duplicate a data stream and write it to file",
+    "",
+    "SYNOPSIS",
+    "    tee,outfile2  infile outfile1",
+    "",
+    "DESCRIPTION",
+    "    This operator copies the input dataset to outfile1 and outfile2. The first output stream",
+    "    in outfile1 can be further processesd with other cdo operators. The second output outfile2",
+    "    is written to disk. It can be used to store intermediate results to a file.",
+    "",
+    "PARAMETER",
+    "    outfile2  STRING Destination filename for the copy of the input file",
+};
+
+const CdoHelp PackHelp = {
+    "NAME",
+    "    pack - Pack data",
+    "",
+    "SYNOPSIS",
+    "    pack[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Packing reduces the data volume by reducing the precision of the stored numbers.",
+    "    It is implemented using the NetCDF attributes add_offset and scale_factor.",
+    "    The operator pack calculates the attributes add_offset and scale_factor for all variables.",
+    "    The default data type for all variables is automatically changed to 16-bit integer.",
+    "    Use the CDO option -b to change the data type to a different integer precision, if needed.",
+    "    Missing values are automatically transformed to the current data type.",
+    "    ",
+    "    Alternatively, the pack parameters add_offset and scale_factor can be read from a file for each variable.",
+    "",
+    "PARAMETER",
+    "    printparam  BOOL    Print pack parameters to stdout for each variable",
+    "    filename    STRING  Read pack parameters from file for each variable[format: name=<> add_offset=<> scale_factor=<>]",
+};
+
+const CdoHelp UnpackHelp = {
+    "NAME",
+    "    unpack - Unpack data",
+    "",
+    "SYNOPSIS",
+    "    unpack  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Packing reduces the data volume by reducing the precision of the stored numbers.",
+    "    It is implemented using the NetCDF attributes add_offset and scale_factor.",
+    "    The operator unpack unpack all packed variables.",
+    "    The default data type for all variables is automatically changed to 32-bit floats.",
+    "    Use the CDO option -b F64 to change the data type to 64-bit floats, if needed.",
+};
+
+const CdoHelp BitroundingHelp = {
+    "NAME",
+    "    bitrounding - Bit rounding",
+    "",
+    "SYNOPSIS",
+    "    bitrounding[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator calculates for each field the number of necessary mantissa bits to get a certain",
+    "    information level in the data. With this number of significant bits (numbits) a rounding of the data is performed.",
+    "    This allows the data to be compressed to a higher level.",
+    "    ",
+    "    The default value of the information level is 0.9999 and can be adjusted with the parameter inflevel.",
+    "    That means 99.99% of the information in the mantissa bits is preserved.",
+    "    ",
+    "    Alternatively, the number of significant bits can be set for all variables with the numbits parameter.",
+    "    Furthermore, numbits can be assigned for each variable via the filename parameter. In this case, numbits is still",
+    "    calculated for all variables if they are not present in the file.",
+    "    ",
+    "    The analysis of the bit information is based on the Julia library BitInformation.jl (https://github.com/milankl/BitInformation.jl).",
+    "    The procedure to derive the number of significant mantissa bits was adapted from the Python library xbitinfo (https://github.com/observingClouds/xbitinfo).",
+    "    Quantize to the number of mantissa bits is done with IEEE rounding using code from NetCDF 4.9.0.",
+    "    ",
+    "    Currently only 32-bit float data is rounded. Data with missing values are not yet supported for the calculation of significant bits.",
+    "",
+    "PARAMETER",
+    "    inflevel   FLOAT   Information level (0 - 1) [default: 0.9999]",
+    "    addbits    INTEGER Add bits to the number of significant bits [default: 0]",
+    "    minbits    INTEGER Minimum value of the number of bits [default: 1]",
+    "    maxbits    INTEGER Maximum value of the number of bits [default: 23]",
+    "    numsteps   INTEGER Set to 1 to run the calculation only in the first time step",
+    "    numbits    INTEGER Set number of significant bits",
+    "    printbits  BOOL    Print max. numbits per variable of 1st timestep to stdout [format: name=numbits]",
+    "    filename   STRING  Read number of significant bits per variable from file [format: name=numbits]",
+};
+
+const CdoHelp ReplaceHelp = {
+    "NAME",
+    "    replace - Replace variables",
+    "",
+    "SYNOPSIS",
+    "    replace  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator replaces variables in infile1 by variables from infile2 and write",
+    "    the result to outfile. Both input datasets need to have the same number of timesteps.",
+    "    All variable names may only occur once!",
+};
+
+const CdoHelp DuplicateHelp = {
+    "NAME",
+    "    duplicate - Duplicates a dataset",
+    "",
+    "SYNOPSIS",
+    "    duplicate[,ndup]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator duplicates the contents of infile and writes the result to outfile.",
+    "    The optional parameter sets the number of duplicates, the default is 2.",
+    "",
+    "PARAMETER",
+    "    ndup  INTEGER  Number of duplicates, default is 2.",
+};
+
+const CdoHelp MergegridHelp = {
+    "NAME",
+    "    mergegrid - Merge grid",
+    "",
+    "SYNOPSIS",
+    "    mergegrid  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Merges grid points of all variables from infile2 to infile1 and write the result to outfile.",
+    "    Only the non missing values of infile2 will be used. The horizontal grid of infile2 should ",
+    "    be smaller or equal to the grid of infile1 and the resolution must be the same.",
+    "    Only rectilinear grids are supported. Both input files need to have the same variables ",
+    "    and the same number of timesteps.",
+};
+
+const CdoHelp MergeHelp = {
+    "NAME",
+    "    merge, mergetime - Merge datasets",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This module reads datasets from several input files, merges them and writes the resulting dataset to outfile.",
+    "",
+    "OPERATORS",
+    "    merge      Merge datasets with different fields",
+    "               Merges time series of different fields from several input datasets. The number ",
+    "               of fields per timestep written to outfile is the sum of the field numbers ",
+    "               per timestep in all input datasets. The time series on all input datasets are ",
+    "               required to have different fields and the same number of timesteps.",
+    "               The fields in each different input file either have to be different variables",
+    "               or different levels of the same variable. A mixture of different variables on",
+    "               different levels in different input files is not allowed.",
+    "    mergetime  Merge datasets sorted by date and time",
+    "               Merges all timesteps of all input files sorted by date and time.",
+    "               All input files need to have the same structure with the same variables on ",
+    "               different timesteps. After this operation every input timestep is in outfile ",
+    "               and all timesteps are sorted by date and time.",
+    "",
+    "ENVIRONMENT",
+    "    SKIP_SAME_TIME",
+    "        If set to 1, skips all consecutive timesteps with a double entry of the same timestamp.",
+    "",
+    "NOTE",
+    "    Operators of this module need to open all input files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp SplitHelp = {
+    "NAME",
+    "    splitcode, splitparam, splitname, splitlevel, splitgrid, splitzaxis, ",
+    "    splittabnum - Split a dataset",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,parameter]  infile obase",
+    "",
+    "DESCRIPTION",
+    "    This module splits infile into pieces. The output files will be named <obase><xxx><suffix>",
+    "    where suffix is the filename extension derived from the file format. xxx and the contents ",
+    "    of the output files depends on the chosen operator. ",
+    "    params is a comma-separated list of processing parameters.",
+    "",
+    "OPERATORS",
+    "    splitcode    Split code numbers",
+    "                 Splits a dataset into pieces, one for each different code number.",
+    "                 xxx will have three digits with the code number.",
+    "    splitparam   Split parameter identifiers",
+    "                 Splits a dataset into pieces, one for each different parameter identifier.",
+    "                 xxx will be a string with the parameter identifier.",
+    "    splitname    Split variable names",
+    "                 Splits a dataset into pieces, one for each variable name.",
+    "                 xxx will be a string with the variable name.",
+    "    splitlevel   Split levels",
+    "                 Splits a dataset into pieces, one for each different level.",
+    "                 xxx will have six digits with the level.",
+    "    splitgrid    Split grids",
+    "                 Splits a dataset into pieces, one for each different grid.",
+    "                 xxx will have two digits with the grid number.",
+    "    splitzaxis   Split z-axes",
+    "                 Splits a dataset into pieces, one for each different z-axis.",
+    "                 xxx will have two digits with the z-axis number.",
+    "    splittabnum  Split parameter table numbers",
+    "                 Splits a dataset into pieces, one for each GRIB1 parameter table number.",
+    "                 xxx will have three digits with the GRIB1 parameter table number.",
+    "",
+    "PARAMETER",
+    "    swap            STRING  Swap the position of obase and xxx in the output filename",
+    "    uuid=<attname>  STRING  Add a UUID as global attribute <attname> to each output file",
+    "",
+    "ENVIRONMENT",
+    "    CDO_FILE_SUFFIX",
+    "        Set the default file suffix. This suffix will be added to the output file ",
+    "        names instead of the filename extension derived from the file format. ",
+    "        Set this variable to NULL to disable the adding of a file suffix.",
+    "",
+    "NOTE",
+    "    Operators of this module need to open all output files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp SplittimeHelp = {
+    "NAME",
+    "    splithour, splitday, splitseas, splityear, splityearmon, splitmon - ",
+    "    Split timesteps of a dataset",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile obase",
+    "    splitmon[,format]  infile obase",
+    "",
+    "DESCRIPTION",
+    "    This module splits infile into  timesteps pieces. The output files will be named",
+    "    <obase><xxx><suffix> where suffix is the filename extension derived from the file format. ",
+    "    xxx and the contents of the output files depends on the chosen operator. ",
+    "",
+    "OPERATORS",
+    "    splithour     Split hours",
+    "                  Splits a file into pieces, one for each different hour.",
+    "                  xxx will have two digits with the hour.",
+    "    splitday      Split days",
+    "                  Splits a file into pieces, one for each different day.",
+    "                  xxx will have two digits with the day.",
+    "    splitseas     Split seasons",
+    "                  Splits a file into pieces, one for each different season.",
+    "                  xxx will have three characters with the season.",
+    "    splityear     Split years",
+    "                  Splits a file into pieces, one for each different year.",
+    "                  xxx will have four digits with the year (YYYY).",
+    "    splityearmon  Split in years and months",
+    "                  Splits a file into pieces, one for each different year and month.",
+    "                  xxx will have six digits with the year and month (YYYYMM).",
+    "    splitmon      Split months",
+    "                  Splits a file into pieces, one for each different month.",
+    "                  xxx will have two digits with the month.",
+    "",
+    "PARAMETER",
+    "    format  STRING  C-style format for strftime() (e.g. %B for the full month name)",
+    "",
+    "ENVIRONMENT",
+    "    CDO_FILE_SUFFIX",
+    "        Set the default file suffix. This suffix will be added to the output file ",
+    "        names instead of the filename extension derived from the file format. ",
+    "        Set this variable to NULL to disable the adding of a file suffix.",
+    "",
+    "NOTE",
+    "    Operators of this module need to open all output files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp SplitselHelp = {
+    "NAME",
+    "    splitsel - Split selected timesteps",
+    "",
+    "SYNOPSIS",
+    "    splitsel,nsets[,noffset[,nskip]]  infile obase",
+    "",
+    "DESCRIPTION",
+    "    This operator splits infile into pieces, one for each adjacent",
+    "    sequence t_1, ...., t_n of timesteps of the same selected time range.",
+    "    The output files will be named <obase><nnnnnn><suffix> where nnnnnn is the ",
+    "    sequence number and suffix is the filename extension derived from the file format.",
+    "",
+    "PARAMETER",
+    "    nsets    INTEGER  Number of input timesteps for each output file",
+    "    noffset  INTEGER  Number of input timesteps skipped before the first timestep range (optional)",
+    "    nskip    INTEGER  Number of input timesteps skipped between timestep ranges (optional)",
+    "",
+    "ENVIRONMENT",
+    "    CDO_FILE_SUFFIX",
+    "        Set the default file suffix. This suffix will be added to the output file ",
+    "        names instead of the filename extension derived from the file format. ",
+    "        Set this variable to NULL to disable the adding of a file suffix.",
+};
+
+const CdoHelp SplitdateHelp = {
+    "NAME",
+    "    splitdate - Splits a file into dates",
+    "",
+    "SYNOPSIS",
+    "    splitdate  infile obase",
+    "",
+    "DESCRIPTION",
+    "    This operator splits infile into pieces, one for each different date.",
+    "    The output files will be named <obase><YYYY-MM-DD><suffix> where YYYY-MM-DD is the ",
+    "    date and suffix is the filename extension derived from the file format.",
+    "",
+    "ENVIRONMENT",
+    "    CDO_FILE_SUFFIX",
+    "        Set the default file suffix. This suffix will be added to the output file ",
+    "        names instead of the filename extension derived from the file format. ",
+    "        Set this variable to NULL to disable the adding of a file suffix.",
+};
+
+const CdoHelp DistgridHelp = {
+    "NAME",
+    "    distgrid - Distribute horizontal grid",
+    "",
+    "SYNOPSIS",
+    "    distgrid,nx[,ny]  infile obase",
+    "",
+    "DESCRIPTION",
+    "    This operator distributes a dataset into smaller pieces. Each output file contains a different region of the",
+    "    horizontal source grid. 2D Lon/Lat grids can be split into nx*ny pieces, where a target grid region contains",
+    "    a structured longitude/latitude box of the source grid. Data on an unstructured grid is split into nx pieces.",
+    "    The output files will be named <obase><xxx><suffix> where suffix is the filename extension derived from the",
+    "    file format. xxx will have five digits with the number of the target region.",
+    "",
+    "PARAMETER",
+    "    nx  INTEGER  Number of regions in x direction, or number of pieces for unstructured grids",
+    "    ny  INTEGER  Number of regions in y direction [default: 1]",
+    "",
+    "NOTE",
+    "    This operator needs to open all output files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp CollgridHelp = {
+    "NAME",
+    "    collgrid - Collect horizontal grid",
+    "",
+    "SYNOPSIS",
+    "    collgrid[,nx[,names]]  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator collects the data of the input files to one output file. All input files need",
+    "    to have the same variables and the same number of timesteps on a different horizonal grid region.",
+    "    If the source regions are on a structured lon/lat grid, all regions together must result in a",
+    "    new structured lat/long grid box. Data on an unstructured grid is concatenated in the order of",
+    "    the input files. The parameter nx needs to be specified only for curvilinear grids.",
+    "",
+    "PARAMETER",
+    "    nx     INTEGER  Number of regions in x direction [default: number of input files]",
+    "    names  STRING   Comma-separated list of variable names [default: all variables]",
+    "",
+    "NOTE",
+    "    This operator needs to open all input files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp SelectHelp = {
+    "NAME",
+    "    select, delete - Select fields",
+    "",
+    "SYNOPSIS",
+    "    <operator>,parameter  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This module selects some fields from infiles and writes them to outfile.",
+    "    infiles is an arbitrary number of input files. All input files need to have ",
+    "    the same structure with the same variables on different timesteps.",
+    "    The fields selected depends on the chosen parameters. Parameter is a comma-separated list",
+    "    of \"key=value\" pairs. A range of integer values can be specified by first/last[/inc].",
+    "    Wildcards are supported for string values.",
+    "",
+    "OPERATORS",
+    "    select  Select fields",
+    "            Selects all fields with parameters in a user given list.",
+    "    delete  Delete fields",
+    "            Deletes all fields with parameters in a user given list.",
+    "",
+    "PARAMETER",
+    "    name              STRING  Comma-separated list of variable names.",
+    "    param             STRING  Comma-separated list of parameter identifiers.",
+    "    code              INTEGER Comma-separated list or first/last[/inc] range of code numbers.",
+    "    level             FLOAT   Comma-separated list of vertical levels.",
+    "    levrange          FLOAT   First and last value of the level range.",
+    "    levidx            INTEGER Comma-separated list or first/last[/inc] range of index of levels.",
+    "    zaxisname         STRING  Comma-separated list of zaxis names.",
+    "    zaxisnum          INTEGER Comma-separated list or first/last[/inc] range of zaxis numbers.",
+    "    ltype             INTEGER Comma-separated list or first/last[/inc] range of GRIB level types.",
+    "    gridname          STRING  Comma-separated list of grid names.",
+    "    gridnum           INTEGER Comma-separated list or first/last[/inc] range of grid numbers.",
+    "    steptype          STRING  Comma-separated list of timestep types (constant, avg, accum, min, max, range, diff, sum)",
+    "    date              STRING  Comma-separated list of dates (format YYYY-MM-DDThh:mm:ss).",
+    "    startdate         STRING  Start date (format YYYY-MM-DDThh:mm:ss).",
+    "    enddate           STRING  End date (format YYYY-MM-DDThh:mm:ss).",
+    "    minute            INTEGER Comma-separated list or first/last[/inc] range of minutes.",
+    "    hour              INTEGER Comma-separated list or first/last[/inc] range of hours.",
+    "    day               INTEGER Comma-separated list or first/last[/inc] range of days.",
+    "    month             INTEGER Comma-separated list or first/last[/inc] range of months.",
+    "    season            STRING  Comma-separated list of seasons (substring of DJFMAMJJASOND or ANN).",
+    "    year              INTEGER Comma-separated list or first/last[/inc] range of years.",
+    "    dom               STRING  Comma-separated list of the day of month (e.g. 29feb).",
+    "    timestep          INTEGER Comma-separated list or first/last[/inc] range of timesteps. Negative values select timesteps from the end (NetCDF only).",
+    "    timestep_of_year  INTEGER Comma-separated list or first/last[/inc] range of timesteps of year.",
+    "    timestepmask      STRING  Read timesteps from a mask file.",
+};
+
+const CdoHelp SelmultiHelp = {
+    "NAME",
+    "    selmulti, delmulti, changemulti - Select multiple fields via GRIB1 parameters",
+    "",
+    "SYNOPSIS",
+    "    <operator>,selection-specification  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module selects multiple fields from infile and writes them to outfile.",
+    "    selection-specification is a filename or in-place string with the selection specification.",
+    "    Each selection-specification has the following compact notation format: ",
+    "    ",
+    "       <type>(parameters; leveltype(s); levels)",
+    "    ",
+    "    type      "    "    sel for select or del for delete (optional)",
+    "    parameters"    "    GRIB1 parameter code number",
+    "    leveltype "    "    GRIB1 level type",
+    "    levels    "    "    value of each level",
+    "    ",
+    "    Examples:",
+    "    ",
+    "       (1; 103; 0) ",
+    "       (33,34; 105; 10) ",
+    "       (11,17; 105; 2) ",
+    "       (71,73,74,75,61,62,65,117,67,122,121,11,131,66,84,111,112; 105; 0) ",
+    "    ",
+    "    The following descriptive notation can also be used for selection specification from a file:",
+    "    ",
+    "       SELECT/DELETE, PARAMETER=parameters, LEVTYPE=leveltye(s), LEVEL=levels",
+    "    ",
+    "    Examples:",
+    "    ",
+    "       SELECT, PARAMETER=1, LEVTYPE=103, LEVEL=0 ",
+    "       SELECT, PARAMETER=33/34, LEVTYPE=105, LEVEL=10 ",
+    "       SELECT, PARAMETER=11/17, LEVTYPE=105, LEVEL=2 ",
+    "       SELECT, PARAMETER=71/73/74/75/61/62/65/117/67/122, LEVTYPE=105, LEVEL=0 ",
+    "       DELETE, PARAMETER=128, LEVTYPE=109, LEVEL=* ",
+    "    ",
+    "    The following will convert Pressure from Pa into hPa; Temp from Kelvin to Celsius: ",
+    "       SELECT, PARAMETER=1, LEVTYPE= 103, LEVEL=0, SCALE=0.01 ",
+    "       SELECT, PARAMETER=11, LEVTYPE=105, LEVEL=2, OFFSET=273.15 ",
+    "    If SCALE and/or OFFSET are defined, then the data values are scaled as SCALE*(VALUE-OFFSET).",
+    "",
+    "OPERATORS",
+    "    selmulti     Select multiple fields",
+    "    delmulti     Delete multiple fields",
+    "    changemulti  Change identication of multiple fields",
+};
+
+const CdoHelp SelvarHelp = {
+    "NAME",
+    "    selparam, delparam, selcode, delcode, selname, delname, selstdname, sellevel, ",
+    "    sellevidx, selgrid, selzaxis, selzaxisname, selltype, seltabnum - Select fields",
+    "",
+    "SYNOPSIS",
+    "    <operator>,parameter  infile outfile",
+    "    selcode,codes  infile outfile",
+    "    delcode,codes  infile outfile",
+    "    selname,names  infile outfile",
+    "    delname,names  infile outfile",
+    "    selstdname,stdnames  infile outfile",
+    "    sellevel,levels  infile outfile",
+    "    sellevidx,levidx  infile outfile",
+    "    selgrid,grids  infile outfile",
+    "    selzaxis,zaxes  infile outfile",
+    "    selzaxisname,zaxisnames  infile outfile",
+    "    selltype,ltypes  infile outfile",
+    "    seltabnum,tabnums  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module selects some fields from infile and writes them to outfile.",
+    "    The fields selected depends on the chosen operator and the parameters. A range of integer",
+    "    values can be specified by first/last[/inc].",
+    "",
+    "OPERATORS",
+    "    selparam      Select parameters by identifier",
+    "                  Selects all fields with parameter identifiers in a user given list.",
+    "    delparam      Delete parameters by identifier",
+    "                  Deletes all fields with parameter identifiers in a user given list.",
+    "    selcode       Select parameters by code number",
+    "                  Selects all fields with code numbers in a user given list or range.",
+    "    delcode       Delete parameters by code number",
+    "                  Deletes all fields with code numbers in a user given list or range.",
+    "    selname       Select parameters by name",
+    "                  Selects all fields with parameter names in a user given list.",
+    "    delname       Delete parameters by name",
+    "                  Deletes all fields with parameter names in a user given list.",
+    "    selstdname    Select parameters by standard name",
+    "                  Selects all fields with standard names in a user given list.",
+    "    sellevel      Select levels",
+    "                  Selects all fields with levels in a user given list.",
+    "    sellevidx     Select levels by index",
+    "                  Selects all fields with index of levels in a user given list or range.",
+    "    selgrid       Select grids",
+    "                  Selects all fields with grids in a user given list.",
+    "    selzaxis      Select z-axes",
+    "                  Selects all fields with z-axes in a user given list.",
+    "    selzaxisname  Select z-axes by name",
+    "                  Selects all fields with z-axis names in a user given list.",
+    "    selltype      Select GRIB level types",
+    "                  Selects all fields with GRIB level type in a user given list or range.",
+    "    seltabnum     Select parameter table numbers",
+    "                  Selects all fields with parameter table numbers in a user given list or range.",
+    "",
+    "PARAMETER",
+    "    parameter   STRING   Comma-separated list of parameter identifiers.",
+    "    codes       INTEGER  Comma-separated list or first/last[/inc] range of code numbers.",
+    "    names       STRING   Comma-separated list of variable names.",
+    "    stdnames    STRING   Comma-separated list of standard names.",
+    "    levels      FLOAT    Comma-separated list of vertical levels.",
+    "    levidx      INTEGER  Comma-separated list or first/last[/inc] range of index of levels.",
+    "    ltypes      INTEGER  Comma-separated list or first/last[/inc] range of GRIB level types.",
+    "    grids       STRING   Comma-separated list of grid names or numbers.",
+    "    zaxes       STRING   Comma-separated list of z-axis types or numbers.",
+    "    zaxisnames  STRING   Comma-separated list of z-axis names.",
+    "    tabnums     INTEGER  Comma-separated list or range of parameter table numbers.",
+};
+
+const CdoHelp SeltimeHelp = {
+    "NAME",
+    "    seltimestep, seltime, selhour, selday, selmonth, selyear, selseason, seldate, ",
+    "    selsmon - Select timesteps",
+    "",
+    "SYNOPSIS",
+    "    seltimestep,timesteps  infile outfile",
+    "    seltime,times  infile outfile",
+    "    selhour,hours  infile outfile",
+    "    selday,days  infile outfile",
+    "    selmonth,months  infile outfile",
+    "    selyear,years  infile outfile",
+    "    selseason,seasons  infile outfile",
+    "    seldate,startdate[,enddate]  infile outfile",
+    "    selsmon,month[,nts1[,nts2]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module selects user specified timesteps from infile and writes them to outfile.",
+    "    The timesteps selected depends on the chosen operator and the parameters. A range of integer values",
+    "    can be specified by first/last[/inc].",
+    "",
+    "OPERATORS",
+    "    seltimestep  Select timesteps",
+    "                 Selects all timesteps with a timestep in a user given list or range.",
+    "    seltime      Select times",
+    "                 Selects all timesteps with a time in a user given list or range.",
+    "    selhour      Select hours",
+    "                 Selects all timesteps with a hour in a user given list or range.",
+    "    selday       Select days",
+    "                 Selects all timesteps with a day in a user given list or range.",
+    "    selmonth     Select months",
+    "                 Selects all timesteps with a month in a user given list or range.",
+    "    selyear      Select years",
+    "                 Selects all timesteps with a year in a user given list or range.",
+    "    selseason    Select seasons",
+    "                 Selects all timesteps with a month of a season in a user given list.",
+    "    seldate      Select dates",
+    "                 Selects all timesteps with a date in a user given range.",
+    "    selsmon      Select single month",
+    "                 Selects a month and optional an arbitrary number of timesteps before and after this month.",
+    "",
+    "PARAMETER",
+    "    timesteps  INTEGER  Comma-separated list or first/last[/inc] range of timesteps. Negative values select timesteps from the end (NetCDF only).",
+    "    times      STRING   Comma-separated list of times (format hh:mm:ss).",
+    "    hours      INTEGER  Comma-separated list or first/last[/inc] range of hours.",
+    "    days       INTEGER  Comma-separated list or first/last[/inc] range of days.",
+    "    months     INTEGER  Comma-separated list or first/last[/inc] range of months.",
+    "    years      INTEGER  Comma-separated list or first/last[/inc] range of years.",
+    "    seasons    STRING   Comma-separated list of seasons (substring of DJFMAMJJASOND or ANN).",
+    "    startdate  STRING   Start date (format YYYY-MM-DDThh:mm:ss).",
+    "    enddate    STRING   End date (format YYYY-MM-DDThh:mm:ss) [default: startdate].",
+    "    nts1       INTEGER  Number of timesteps before the selected month [default: 0].",
+    "    nts2       INTEGER  Number of timesteps after the selected month [default: nts1].",
+};
+
+const CdoHelp SelboxHelp = {
+    "NAME",
+    "    sellonlatbox, selindexbox - Select a box",
+    "",
+    "SYNOPSIS",
+    "    sellonlatbox,lon1,lon2,lat1,lat2  infile outfile",
+    "    selindexbox,idx1,idx2,idy1,idy2  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Selects grid cells inside a lon/lat or index box.",
+    "",
+    "OPERATORS",
+    "    sellonlatbox  Select a longitude/latitude box",
+    "                  Selects grid cells inside a lon/lat box. The user must specify the longitude and latitude of the edges of the box.",
+    "                  Only those grid cells are considered whose grid center lies within the lon/lat box.",
+    "                  For rotated lon/lat grids the parameters must be specified in rotated coordinates.",
+    "    selindexbox   Select an index box",
+    "                  Selects grid cells within an index box. The user must specify the indices of the edges of the box.",
+    "                  The index of the left edge can be greater then the one of the right edge. Use negative indexing to",
+    "                  start from the end. The input grid must be a regular lon/lat or a 2D curvilinear grid.",
+    "",
+    "PARAMETER",
+    "    lon1  FLOAT    Western longitude in degrees",
+    "    lon2  FLOAT    Eastern longitude in degrees",
+    "    lat1  FLOAT    Southern or northern latitude in degrees",
+    "    lat2  FLOAT    Northern or southern latitude in degrees",
+    "    idx1  INTEGER  Index of first longitude (1 - nlon)",
+    "    idx2  INTEGER  Index of last longitude (1 - nlon)",
+    "    idy1  INTEGER  Index of first latitude (1 - nlat)",
+    "    idy2  INTEGER  Index of last latitude (1 - nlat)",
+};
+
+const CdoHelp SelregionHelp = {
+    "NAME",
+    "    selregion, selcircle - Select horizontal regions",
+    "",
+    "SYNOPSIS",
+    "    selregion,regions  infile outfile",
+    "    selcircle[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Selects all grid cells with the center point inside user defined regions or a circle.",
+    "    The resulting grid is unstructured.",
+    "",
+    "OPERATORS",
+    "    selregion  Select cells inside regions",
+    "               Selects all grid cells with the center point inside the regions.",
+    "               Regions can be defined by the user via an ASCII file.",
+    "               Each region consists of the geographic coordinates of a convex polygon.",
+    "               Each line of a polygon description file contains the longitude and latitude of one point.",
+    "               Each polygon description file can contain one or more polygons separated by a line with the character \\&.",
+    "               ",
+    "               Predefined regions of countries can be specified via the country codes.",
+    "               A country is specified with dcw:<CountryCode>. Country codes can be combined with the plus sign.",
+    "    selcircle  Select cells inside a circle",
+    "               Selects all grid cells with the center point inside a circle. The circle is described by geographic coordinates",
+    "               of the center and the radius of the circle.",
+    "",
+    "PARAMETER",
+    "    regions  STRING   Comma-separated list of ASCII formatted files with different regions",
+    "    lon      FLOAT    Longitude of the center of the circle in degrees, default lon=0.0",
+    "    lat      FLOAT    Latitude of the center of the circle in degrees, default lat=0.0",
+    "    radius   STRING   Radius of the circle, default radius=1deg (units: deg, rad, km, m)",
+};
+
+const CdoHelp SelgridcellHelp = {
+    "NAME",
+    "    selgridcell, delgridcell - Select grid cells",
+    "",
+    "SYNOPSIS",
+    "    <operator>,indices  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    The operator selects grid cells of all fields from infile. The user must specify the index of each grid cell.",
+    "    The resulting grid in outfile is unstructured.",
+    "",
+    "OPERATORS",
+    "    selgridcell  Select grid cells",
+    "    delgridcell  Delete grid cells",
+    "",
+    "PARAMETER",
+    "    indices  INTEGER  Comma-separated list or first/last[/inc] range of indices",
+};
+
+const CdoHelp SamplegridHelp = {
+    "NAME",
+    "    samplegrid - Resample grid",
+    "",
+    "SYNOPSIS",
+    "    samplegrid,factor  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This is a special operator for resampling the horizontal grid.",
+    "    No interpolation takes place. Resample factor=2 means every second grid point is removed.",
+    "    Only rectilinear and curvilinear source grids are supported by this operator.",
+    "",
+    "PARAMETER",
+    "    factor  INTEGER  Resample factor, typically 2, which will half the resolution",
+};
+
+const CdoHelp SelyearidxHelp = {
+    "NAME",
+    "    selyearidx - Select year by index",
+    "",
+    "SYNOPSIS",
+    "    selyearidx  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Selects field elements from infile2 by a yearly time index from infile1.",
+    "    The yearly indices in infile1 should be the result of corresponding yearminidx and yearmaxidx operations, respectively.",
+};
+
+const CdoHelp SelsurfaceHelp = {
+    "NAME",
+    "    bottomvalue, topvalue, isosurface - Extract surface",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "    isosurface,isovalue  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes a surface from all 3D variables. The result is a horizonal 2D field.",
+    "",
+    "OPERATORS",
+    "    bottomvalue  Extract bottom level",
+    "                 This operator selects the valid values at the bottom level.",
+    "                 The NetCDF CF compliant attribute positive is used to determine where top and bottom are.",
+    "                 If this attribute is missing, low values are bottom and high values are top.",
+    "    topvalue     Extract top level",
+    "                 This operator selects the valid values at the top level.",
+    "                 The NetCDF CF compliant attribute positive is used to determine where top and bottom are.",
+    "                 If this attribute is missing, low values are bottom and high values are top.",
+    "    isosurface   Extract isosurface",
+    "                 This operator computes an isosurface. The value of the isosurfce is specified by the parameter isovalue.",
+    "                 The isosurface is calculated by linear interpolation between two layers.",
+    "",
+    "PARAMETER",
+    "    isovalue  FLOAT  Isosurface value",
+};
+
+const CdoHelp CondHelp = {
+    "NAME",
+    "    ifthen, ifnotthen - Conditional select one field",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module selects field elements from infile2 with respect to infile1 and writes them ",
+    "    to outfile. The fields in infile1 are handled as a mask. A value ",
+    "    not equal to zero is treated as \"true\", zero is treated as \"false\".",
+    "    The number of fields in infile1 has either to be the same as in infile2 or the",
+    "    same as in one timestep of infile2 or only one.",
+    "    The fields in outfile inherit the meta data from infile2.",
+    "",
+    "OPERATORS",
+    "    ifthen     If then",
+    "                        / i_2(t,x) if i_1(t,x) NE 0  AND  i_1(t,x) NE miss",
+    "               o(t,x) =",
+    "                        \\ miss     if i_1(t,x) EQ 0  OR   i_1(t,x) EQ miss",
+    "    ifnotthen  If not then",
+    "                        / i_2(t,x) if i_1(t,x) EQ 0  AND  i_1(t,x) NE miss",
+    "               o(t,x) = ",
+    "                        \\ miss     if i_1(t,x) NE 0  OR   i_1(t,x) EQ miss",
+};
+
+const CdoHelp Cond2Help = {
+    "NAME",
+    "    ifthenelse - Conditional select  two fields",
+    "",
+    "SYNOPSIS",
+    "    ifthenelse  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator selects field elements from infile2 or infile3 with respect to",
+    "    infile1 and writes them to outfile. The fields in infile1 are handled as a mask.",
+    "    A value not equal to zero is treated as \"true\", zero is treated as \"false\".",
+    "    The number of fields in infile1 has either to be the same as in infile2 or the ",
+    "    same as in one timestep of infile2 or only one.",
+    "    infile2 and infile3 need to have the same number of fields.",
+    "    The fields in outfile inherit the meta data from infile2.",
+    "    ",
+    "              / i_2(t,x) if i_1(t,x) NE 0  AND  i_1(t,x) NE miss",
+    "    o(t,x) = <  i_3(t,x) if i_1(t,x) EQ 0  AND  i_1[t,x) NE miss",
+    "              \\ miss     if i_1(t,x) EQ miss",
+};
+
+const CdoHelp CondcHelp = {
+    "NAME",
+    "    ifthenc, ifnotthenc - Conditional select a constant",
+    "",
+    "SYNOPSIS",
+    "    <operator>,c  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module creates fields with a constant value or missing value.",
+    "    The fields in infile are handled as a mask. A value not equal ",
+    "    to zero is treated as \"true\", zero is treated as \"false\".",
+    "",
+    "OPERATORS",
+    "    ifthenc     If then constant",
+    "                         / c      if i(t,x) NE 0  AND  i(t,x) NE miss",
+    "                o(t,x) =",
+    "                         \\ miss   if i(t,x) EQ 0  OR   i(t,x) EQ miss",
+    "    ifnotthenc  If not then constant",
+    "                         / c      if i(t,x) EQ 0  AND  i(t,x) NE miss",
+    "                o(t,x) =",
+    "                         \\ miss   if i(t,x) NE 0  OR   i(t,x) EQ miss",
+    "",
+    "PARAMETER",
+    "    c  FLOAT  Constant",
+};
+
+const CdoHelp MapReduceHelp = {
+    "NAME",
+    "    reducegrid - Reduce fields to user-defined mask",
+    "",
+    "SYNOPSIS",
+    "    reducegrid,mask[,limitCoordsOutput]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module holds an operator for data reduction based on a user defined mask.",
+    "    The output grid is unstructured and includes coordinate bounds. Bounds can be",
+    "    avoided by using the additional 'nobounds' keyword. With 'nocoords' given,",
+    "    coordinates a completely suppressed.",
+    "",
+    "PARAMETER",
+    "    mask               STRING file which holds the mask field",
+    "    limitCoordsOutput  STRING optional parameter to limit coordinates output: 'nobounds' disables coordinate bounds, 'nocoords' avoids all coordinate information",
+};
+
+const CdoHelp CompHelp = {
+    "NAME",
+    "    eq, ne, le, lt, ge, gt - Comparison of two fields",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module compares two datasets field by field.",
+    "    The resulting field is a mask containing 1 if the comparison is true and 0 if not.",
+    "    The number of fields in infile1 should be the same as in infile2.",
+    "    One of the input files can contain only one timestep or one field.",
+    "    The fields in outfile inherit the meta data from infile1 or infile2.",
+    "    The type of comparison depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    eq  Equal",
+    "                  /   1   if i_1(t,x) EQ i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "        o(t,x) = <    0   if i_1(t,x) NE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
+    "    ne  Not equal",
+    "                  /   1   if i_1(t,x) NE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "        o(t,x) = <    0   if i_1(t,x) EQ i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
+    "    le  Less equal",
+    "                  /   1   if i_1(t,x) LE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "        o(t,x) = <    0   if i_1(t,x) GT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
+    "    lt  Less than",
+    "                  /   1   if i_1(t,x) LT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "        o(t,x) = <    0   if i_1(t,x) GE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
+    "    ge  Greater equal",
+    "                  /   1   if i_1(t,x) GE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "        o(t,x) = <    0   if i_1(t,x) LT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
+    "    gt  Greater than",
+    "                  /   1   if i_1(t,x) GT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "        o(t,x) = <    0   if i_1(t,x) LE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
+    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
+};
+
+const CdoHelp CompcHelp = {
+    "NAME",
+    "    eqc, nec, lec, ltc, gec, gtc - Comparison of a field with a constant",
+    "",
+    "SYNOPSIS",
+    "    <operator>,c  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module compares all fields of a dataset with a constant. The resulting",
+    "    field is a mask containing 1 if the comparison is true and 0 if not.",
+    "    The type of comparison depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    eqc  Equal constant",
+    "                   /   1   if i(t,x) EQ c     AND  i(t,x),c NE miss",
+    "         o(t,x) = <    0   if i(t,x) NE c     AND  i(t,x),c NE miss",
+    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
+    "    nec  Not equal constant",
+    "                   /   1   if i(t,x) NE c     AND  i(t,x),c NE miss",
+    "         o(t,x) = <    0   if i(t,x) EQ c     AND  i(t,x),c NE miss",
+    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
+    "    lec  Less equal constant",
+    "                   /   1   if i(t,x) LE c     AND  i(t,x),c NE miss",
+    "         o(t,x) = <    0   if i(t,x) GT c     AND  i(t,x),c NE miss",
+    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
+    "    ltc  Less than constant",
+    "                   /   1   if i(t,x) LT c     AND  i(t,x),c NE miss",
+    "         o(t,x) = <    0   if i(t,x) GE c     AND  i(t,x),c NE miss",
+    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
+    "    gec  Greater equal constant",
+    "                   /   1   if i(t,x) GE c     AND  i(t,x),c NE miss",
+    "         o(t,x) = <    0   if i(t,x) LT c     AND  i(t,x),c NE miss",
+    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
+    "    gtc  Greater than constant",
+    "                   /   1   if i(t,x) GT c     AND  i(t,x),c NE miss",
+    "         o(t,x) = <    0   if i(t,x) LE c     AND  i(t,x),c NE miss",
+    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
+    "",
+    "PARAMETER",
+    "    c  FLOAT  Constant",
+};
+
+const CdoHelp YmoncompHelp = {
+    "NAME",
+    "    ymoneq, ymonne, ymonle, ymonlt, ymonge, ymongt - Multi-year monthly comparison",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs compaisons of a time series and one timestep with the same month of year.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same month of year is used.",
+    "    The resulting field is a mask containing 1 if the comparison is true and 0 if not. ",
+    "    The type of comparison depends on the chosen operator.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module YMONSTAT.",
+    "",
+    "OPERATORS",
+    "    ymoneq  Compare time series with Equal",
+    "            Compares whether a time series is equal to a multi-year monthly time series.",
+    "    ymonne  Compare time series with NotEqual",
+    "            Compares whether a time series is not equal to a multi-year monthly time series.",
+    "    ymonle  Compare time series with LessEqual",
+    "            Compares whether a time series is less than or equal to a multi-year monthly time series.",
+    "    ymonlt  Compares if time series with LessThan",
+    "            Compares whether a time series is less than a multi-year monthly time series.",
+    "    ymonge  Compares if time series with GreaterEqual",
+    "            Compares whether a time series is greater than or equal to a multi-year monthly time series.",
+    "    ymongt  Compares if time series with GreaterThan",
+    "            Compares whether a time series is greater than a multi-year monthly time series.",
+};
+
+const CdoHelp SetattributeHelp = {
+    "NAME",
+    "    setattribute - Set attributes",
+    "",
+    "SYNOPSIS",
+    "    setattribute,attributes  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator sets attributes of a dataset and writes the result to outfile.",
+    "    The new attributes are only available in outfile if the file format supports attributes.",
+    "    ",
+    "    Each attribute has the following structure:",
+    "    ",
+    "      [var_nm@]att_nm[:{s|d|i}]=[att_val|{[var_nm@]att_nm}]",
+    "    ",
+    "       var_nm  Variable name (optional). Example: pressure",
+    "       att_nm  Attribute name. Example: units",
+    "       att_val Comma-separated list of attribute values. Example: pascal",
+    "    ",
+    "    The value of var_nm is the name of the variable containing the attribute (named att_nm) that",
+    "    you want to set. Use wildcards to set the attribute att_nm to more than one variable.",
+    "    A value of var_nm of '*' will set the attribute att_nm to all data variables.",
+    "    If var_nm is missing then att_nm refers to a global attribute.",
+    "    ",
+    "    The value of att_nm is the name of the attribute you want to set. For each attribute a string (att_nm:s),",
+    "    a double (att_nm:d) or an integer (att_nm:i) type can be defined. By default the native type is set.",
+    "    ",
+    "    The value of att_val is the contents of the attribute att_nm. att_val may be a single value",
+    "    or one-dimensional array of elements. The type and the number of elements of an attribute will be detected",
+    "    automatically from the contents of the values. An already existing attribute att_nm will be overwritten",
+    "    or it will be removed if att_val is omitted. Alternatively, the values of an existing attribute can be copied.",
+    "    This attribute must then be enclosed in curly brackets.",
+    "    ",
+    "    A special meaning has the attribute name FILE. If this is the 1st attribute then all attributes",
+    "    are read from a file specified in the value of att_val.",
+    "",
+    "PARAMETER",
+    "    attributes  STRING  Comma-separated list of attributes. ",
+    "",
+    "NOTE",
+    "    Attributes are evaluated by CDO when opening infile. Therefor the result of this operator is not available",
+    "    for other operators when this operator is used in chaining operators.",
+};
+
+const CdoHelp SetpartabHelp = {
+    "NAME",
+    "    setpartabp, setpartabn - Set parameter table",
+    "",
+    "SYNOPSIS",
+    "    <operator>,table[,convert]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module transforms data and metadata of infile via a parameter table and writes the result to outfile.",
+    "    A parameter table is an ASCII formatted file with a set of parameter entries for each variable. Each new set have to",
+    "    start with \"\\&parameter\" and to end with \"/\".",
+    "    ",
+    "    The following parameter table entries are supported:",
+    "    ",
+    "     Entry           & Type        & Description      ",
+    "     name            & WORD        & Name of the variable",
+    "     out_name        & WORD        & New name of the variable",
+    "     param           & WORD        & Parameter identifier (GRIB1: code[.tabnum];  GRIB2: num[.cat[.dis]])",
+    "     out_param       & WORD        & New parameter identifier",
+    "     type            & WORD        & Data type (real or double)",
+    "     standard_name   & WORD        & As defined in the CF standard name table",
+    "     long_name       & STRING      & Describing the variable",
+    "     units           & STRING      & Specifying the units for the variable",
+    "     comment         & STRING      & Information concerning the variable",
+    "     cell_methods    & STRING      & Information concerning calculation of means or climatologies",
+    "     cell_measures   & STRING      & Indicates the names of the variables containing cell areas and volumes",
+    "     missing_value   & FLOAT       & Specifying how missing data will be identified",
+    "     valid_min       & FLOAT       & Minimum valid value",
+    "     valid_max       & FLOAT       & Maximum valid value",
+    "     ok_min_mean_abs & FLOAT       & Minimum absolute mean",
+    "     ok_max_mean_abs & FLOAT       & Maximum absolute mean",
+    "     factor          & FLOAT       & Scale factor",
+    "     delete          & INTEGER     & Set to 1 to delete variable",
+    "     convert         & INTEGER     & Set to 1 to convert the unit if necessary",
+    "    ",
+    "    Unsupported parameter table entries are stored as variable attributes.",
+    "    The search key for the variable depends on the operator. Use setpartabn to search variables by the name.",
+    "    This is typically used for NetCDF datasets. The operator setpartabp searches variables by the parameter ID.",
+    "",
+    "OPERATORS",
+    "    setpartabp  Set parameter table",
+    "                Search variables by the parameter identifier.",
+    "    setpartabn  Set parameter table",
+    "                Search variables by name.",
+    "",
+    "PARAMETER",
+    "    table    STRING   Parameter table file or name",
+    "    convert  STRING   Converts the units if necessary",
+};
+
+const CdoHelp SetHelp = {
+    "NAME",
+    "    setcodetab, setcode, setparam, setname, setunit, setlevel, setltype, ",
+    "    setmaxsteps - Set field info",
+    "",
+    "SYNOPSIS",
+    "    setcodetab,table  infile outfile",
+    "    setcode,code  infile outfile",
+    "    setparam,param  infile outfile",
+    "    setname,name  infile outfile",
+    "    setunit,unit  infile outfile",
+    "    setlevel,level  infile outfile",
+    "    setltype,ltype  infile outfile",
+    "    setmaxsteps,maxsteps  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module sets some field information. Depending on the chosen operator the ",
+    "    parameter table, code number, parameter identifier, variable name or level is set.",
+    "",
+    "OPERATORS",
+    "    setcodetab   Set parameter code table",
+    "                 Sets the parameter code table for all variables.",
+    "    setcode      Set code number",
+    "                 Sets the code number for all variables to the same given value.",
+    "    setparam     Set parameter identifier",
+    "                 Sets the parameter identifier of the first variable.",
+    "    setname      Set variable name",
+    "                 Sets the name of the first variable.",
+    "    setunit      Set variable unit",
+    "                 Sets the unit of the first variable.",
+    "    setlevel     Set level",
+    "                 Sets the first level of all variables.",
+    "    setltype     Set GRIB level type",
+    "                 Sets the GRIB level type of all variables.",
+    "    setmaxsteps  Set max timesteps",
+    "                 Sets maximum number of timesteps",
+    "",
+    "PARAMETER",
+    "    table     STRING   Parameter table file or name",
+    "    code      INTEGER  Code number",
+    "    param     STRING   Parameter identifier (GRIB1: code[.tabnum]; GRIB2: num[.cat[.dis]])",
+    "    name      STRING   Variable name",
+    "    level     FLOAT    New level",
+    "    ltype     INTEGER  GRIB level type",
+    "    maxsteps  INTEGER  Maximum number of timesteps",
+};
+
+const CdoHelp SettimeHelp = {
+    "NAME",
+    "    setdate, settime, setday, setmon, setyear, settunits, settaxis, settbounds, ",
+    "    setreftime, setcalendar, shifttime - Set time",
+    "",
+    "SYNOPSIS",
+    "    setdate,date  infile outfile",
+    "    settime,time  infile outfile",
+    "    setday,day  infile outfile",
+    "    setmon,month  infile outfile",
+    "    setyear,year  infile outfile",
+    "    settunits,units  infile outfile",
+    "    settaxis,date,time[,inc]  infile outfile",
+    "    settbounds,frequency  infile outfile",
+    "    setreftime,date,time[,units]  infile outfile",
+    "    setcalendar,calendar  infile outfile",
+    "    shifttime,shiftValue  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module sets the time axis or part of the time axis. Which part of the time axis is",
+    "    overwritten/created depends on the chosen operator. The number of time steps does not change.",
+    "",
+    "OPERATORS",
+    "    setdate      Set date",
+    "                 Sets the date in every timestep to the same given value.",
+    "    settime      Set time of the day",
+    "                 Sets the time in every timestep to the same given value.",
+    "    setday       Set day",
+    "                 Sets the day in every timestep to the same given value.",
+    "    setmon       Set month",
+    "                 Sets the month in every timestep to the same given value.",
+    "    setyear      Set year",
+    "                 Sets the year in every timestep to the same given value.",
+    "    settunits    Set time units",
+    "                 Sets the base units of a relative time axis.",
+    "    settaxis     Set time axis",
+    "                 Sets the time axis.",
+    "    settbounds   Set time bounds",
+    "                 Sets the time bounds.",
+    "    setreftime   Set reference time",
+    "                 Sets the reference time of a relative time axis.",
+    "    setcalendar  Set calendar",
+    "                 Sets the calendar attribute of a relative time axis.",
+    "    shifttime    Shift timesteps",
+    "                 Shifts all timesteps by the parameter shiftValue.",
+    "",
+    "PARAMETER",
+    "    day         INTEGER  Value of the new day",
+    "    month       INTEGER  Value of the new month",
+    "    year        INTEGER  Value of the new year",
+    "    units       STRING   Base units of the time axis (seconds, minutes, hours, days, months, years)",
+    "    date        STRING   Date (format: YYYY-MM-DD)",
+    "    time        STRING   Time (format: hh:mm:ss)",
+    "    inc         STRING   Optional increment (seconds, minutes, hours, days, months, years) [default: 1hour]",
+    "    frequency   STRING   Frequency of the time series (hour, day, month, year)",
+    "    calendar    STRING   Calendar (standard, proleptic_gregorian, 360_day, 365_day, 366_day)",
+    "    shiftValue  STRING   Shift value (e.g. -3hour)",
+};
+
+const CdoHelp ChangeHelp = {
+    "NAME",
+    "    chcode, chparam, chname, chunit, chlevel, chlevelc, chlevelv - ",
+    "    Change field header",
+    "",
+    "SYNOPSIS",
+    "    chcode,oldcode,newcode[,...]  infile outfile",
+    "    chparam,oldparam,newparam,...  infile outfile",
+    "    chname,oldname,newname,...  infile outfile",
+    "    chunit,oldunit,newunit,...  infile outfile",
+    "    chlevel,oldlev,newlev,...  infile outfile",
+    "    chlevelc,code,oldlev,newlev  infile outfile",
+    "    chlevelv,name,oldlev,newlev  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module reads fields from infile, changes some header values",
+    "    and writes the results to outfile. The kind of changes depends on ",
+    "    the chosen operator.",
+    "",
+    "OPERATORS",
+    "    chcode    Change code number",
+    "              Changes some user given code numbers to new user given values.",
+    "    chparam   Change parameter identifier",
+    "              Changes some user given parameter identifiers to new user given values.",
+    "    chname    Change variable or coordinate name",
+    "              Changes some user given variable or coordinate names to new user given names.",
+    "    chunit    Change variable unit",
+    "              Changes some user given variable units to new user given units.",
+    "    chlevel   Change level",
+    "              Changes some user given levels to new user given values.",
+    "    chlevelc  Change level of one code",
+    "              Changes one level of a user given code number.",
+    "    chlevelv  Change level of one variable",
+    "              Changes one level of a user given variable name.",
+    "",
+    "PARAMETER",
+    "    code                   INTEGER  Code number",
+    "    oldcode,newcode,...    INTEGER  Pairs of old and new code numbers",
+    "    oldparam,newparam,...  STRING   Pairs of old and new parameter identifiers",
+    "    name                   STRING   Variable name",
+    "    oldname,newname,...    STRING   Pairs of old and new variable names",
+    "    oldlev                 FLOAT    Old level",
+    "    newlev                 FLOAT    New level",
+    "    oldlev,newlev,...      FLOAT    Pairs of old and new levels",
+};
+
+const CdoHelp SetgridHelp = {
+    "NAME",
+    "    setgrid, setgridtype, setgridarea, setgridmask - Set grid information",
+    "",
+    "SYNOPSIS",
+    "    setgrid,grid  infile outfile",
+    "    setgridtype,gridtype  infile outfile",
+    "    setgridarea,gridarea  infile outfile",
+    "    setgridmask,gridmask  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module modifies the metadata of the horizontal grid. Depending on the chosen operator",
+    "    a new grid description is set, the coordinates are converted or the grid cell area is added.",
+    "",
+    "OPERATORS",
+    "    setgrid      Set grid",
+    "                 Sets a new grid description. The input fields need to have the same grid size as the size",
+    "                 of the target grid description.",
+    "    setgridtype  Set grid type",
+    "                 Sets the grid type of all input fields. The following grid types are available:",
+    "                 curvilinear "    "    Converts a regular grid to a curvilinear grid",
+    "                 unstructured"    "    Converts a regular or curvilinear grid to an unstructured grid",
+    "                 dereference "    "    Dereference a reference to a grid",
+    "                 regular     "    "    Linear interpolation of a reduced Gaussian grid to a regular Gaussian grid",
+    "                 regularnn   "    "    Nearest neighbor interpolation of a reduced Gaussian grid to a regular Gaussian grid",
+    "                 lonlat      "    "    Converts a regular lonlat grid stored as a curvilinear grid back to a lonlat grid",
+    "                 projection  "    "    Removes the geographical coordinates if projection parameter available",
+    "    setgridarea  Set grid cell area",
+    "                 Sets the grid cell area. The parameter gridarea is the path to a data file,",
+    "                 the first field is used as grid cell area. The input fields need to have the same",
+    "                 grid size as the grid cell area. The grid cell area is used to compute",
+    "                 the weights of each grid cell if needed by an operator, e.g. for fldmean.",
+    "    setgridmask  Set grid mask",
+    "                 Sets the grid mask. The parameter gridmask is the path to a data file,",
+    "                 the first field is used as the grid mask. The input fields need to have the same",
+    "                 grid size as the grid mask. The grid mask is used as the target grid mask for",
+    "                 remapping, e.g. for remapbil.",
+    "",
+    "PARAMETER",
+    "    grid      STRING  Grid description file or name",
+    "    gridtype  STRING  Grid type (curvilinear, unstructured, regular, lonlat, projection or dereference)",
+    "    gridarea  STRING  Data file, the first field is used as grid cell area",
+    "    gridmask  STRING  Data file, the first field is used as grid mask",
+};
+
+const CdoHelp SetzaxisHelp = {
+    "NAME",
+    "    setzaxis, genlevelbounds - Set z-axis information",
+    "",
+    "SYNOPSIS",
+    "    setzaxis,zaxis  infile outfile",
+    "    genlevelbounds[,zbot[,ztop]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module modifies the metadata of the vertical grid.",
+    "",
+    "OPERATORS",
+    "    setzaxis        Set z-axis",
+    "                    This operator sets the z-axis description of all variables with the same number of level as the new z-axis.",
+    "    genlevelbounds  Generate level bounds",
+    "                    Generates the layer bounds of the z-axis.",
+    "",
+    "PARAMETER",
+    "    zaxis  STRING  Z-axis description file or name of the target z-axis",
+    "    zbot   FLOAT   Specifying the bottom of the vertical column. Must have the same units as z-axis. ",
+    "    ztop   FLOAT   Specifying the top of the vertical column. Must have the same units as z-axis. ",
+};
+
+const CdoHelp InvertHelp = {
+    "NAME",
+    "    invertlat - Invert latitudes",
+    "",
+    "SYNOPSIS",
+    "    invertlat  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator inverts the latitudes of all fields on a rectilinear grid. ",
+};
+
+const CdoHelp InvertlevHelp = {
+    "NAME",
+    "    invertlev - Invert levels",
+    "",
+    "SYNOPSIS",
+    "    invertlev  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator inverts the levels of all 3D variables.",
+};
+
+const CdoHelp ShiftxyHelp = {
+    "NAME",
+    "    shiftx, shifty - Shift field",
+    "",
+    "SYNOPSIS",
+    "    <operator>,<nshift>,<cyclic>,<coord>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators to shift all fields in x or y direction.",
+    "    All fields need to have the same horizontal rectilinear or curvilinear grid.",
+    "",
+    "OPERATORS",
+    "    shiftx  Shift x",
+    "            Shifts all fields in x direction.",
+    "    shifty  Shift y",
+    "            Shifts all fields in y direction.",
+    "",
+    "PARAMETER",
+    "    nshift  INTEGER  Number of grid cells to shift (default: 1)",
+    "    cyclic  STRING   If set, cells are filled up cyclic (default: missing value)",
+    "    coord   STRING   If set, coordinates are also shifted",
+};
+
+const CdoHelp MaskregionHelp = {
+    "NAME",
+    "    maskregion - Mask regions",
+    "",
+    "SYNOPSIS",
+    "    maskregion,regions  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Masks different regions of the input fields.",
+    "    The grid cells inside a region are untouched, the cells outside are set to missing value.",
+    "    Considered are only those grid cells with the grid center inside the regions.",
+    "    All input fields must have the same horizontal grid.",
+    "    ",
+    "    Regions can be defined by the user via an ASCII file.",
+    "    Each region consists of the geographic coordinates of a convex polygon.",
+    "    Each line of a polygon description file contains the longitude and latitude of one point.",
+    "    Each polygon description file can contain one or more polygons separated by a line with the character \\&.",
+    "    ",
+    "    Predefined regions of countries can be specified via the country codes.",
+    "    A country is specified with dcw:<CountryCode>. Country codes can be combined with the plus sign.",
+    "",
+    "PARAMETER",
+    "    regions  STRING   Comma-separated list of ASCII formatted files with different regions",
+};
+
+const CdoHelp MaskboxHelp = {
+    "NAME",
+    "    masklonlatbox, maskindexbox - Mask a box",
+    "",
+    "SYNOPSIS",
+    "    masklonlatbox,lon1,lon2,lat1,lat2  infile outfile",
+    "    maskindexbox,idx1,idx2,idy1,idy2  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Masks grid cells inside a lon/lat or index box. The elements inside the box are untouched, the ",
+    "    elements outside are set to missing value. All input fields need to have the same horizontal grid.",
+    "    Use sellonlatbox or selindexbox if only the data inside the box are needed.",
+    "",
+    "OPERATORS",
+    "    masklonlatbox  Mask a longitude/latitude box",
+    "                   Masks grid cells inside a lon/lat box. The user must specify the longitude and latitude of the edges of the box.",
+    "                   Only those grid cells are considered whose grid center lies within the lon/lat box.",
+    "                   For rotated lon/lat grids the parameters must be specified in rotated coordinates.",
+    "    maskindexbox   Mask an index box",
+    "                   Masks grid cells within an index box. The user must specify the indices of the edges of the box.",
+    "                   The index of the left edge can be greater then the one of the right edge. Use negative indexing to",
+    "                   start from the end. The input grid must be a regular lon/lat or a 2D curvilinear grid.",
+    "",
+    "PARAMETER",
+    "    lon1  FLOAT    Western longitude",
+    "    lon2  FLOAT    Eastern longitude",
+    "    lat1  FLOAT    Southern or northern latitude",
+    "    lat2  FLOAT    Northern or southern latitude",
+    "    idx1  INTEGER  Index of first longitude",
+    "    idx2  INTEGER  Index of last longitude",
+    "    idy1  INTEGER  Index of first latitude",
+    "    idy2  INTEGER  Index of last latitude",
+};
+
+const CdoHelp SetboxHelp = {
+    "NAME",
+    "    setclonlatbox, setcindexbox - Set a box to constant",
+    "",
+    "SYNOPSIS",
+    "    setclonlatbox,c,lon1,lon2,lat1,lat2  infile outfile",
+    "    setcindexbox,c,idx1,idx2,idy1,idy2  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Sets a box of the rectangularly understood field to a constant value. The elements outside ",
+    "    the box are untouched, the elements inside are set to the given constant. All input fields ",
+    "    need to have the same horizontal grid.",
+    "",
+    "OPERATORS",
+    "    setclonlatbox  Set a longitude/latitude box to constant",
+    "                   Sets the values of a longitude/latitude box to a constant value. The ",
+    "                   user has to give the longitudes and latitudes of the edges of the box.",
+    "    setcindexbox   Set an index box to constant",
+    "                   Sets the values of an index box to a constant value. The user has to ",
+    "                   give the indices of the edges of the box. The index of the left edge ",
+    "                   can be greater than the one of the right edge.",
+    "",
+    "PARAMETER",
+    "    c     FLOAT    Constant",
+    "    lon1  FLOAT    Western longitude",
+    "    lon2  FLOAT    Eastern longitude",
+    "    lat1  FLOAT    Southern or northern latitude",
+    "    lat2  FLOAT    Northern or southern latitude",
+    "    idx1  INTEGER  Index of first longitude",
+    "    idx2  INTEGER  Index of last longitude",
+    "    idy1  INTEGER  Index of first latitude",
+    "    idy2  INTEGER  Index of last latitude",
+};
+
+const CdoHelp EnlargeHelp = {
+    "NAME",
+    "    enlarge - Enlarge fields",
+    "",
+    "SYNOPSIS",
+    "    enlarge,grid  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Enlarge all fields of infile to a user given horizontal grid. Normally only the last ",
+    "    field element is used for the enlargement. If however the input and output",
+    "    grid are regular lon/lat grids, a zonal or meridional enlargement is possible.",
+    "    Zonal enlargement takes place, if the xsize of the input field is 1 and ",
+    "    the ysize of both grids are the same. For meridional enlargement the ysize",
+    "    have to be 1 and the xsize of both grids should have the same size.",
+    "",
+    "PARAMETER",
+    "    grid  STRING  Target grid description file or name",
+};
+
+const CdoHelp SetmissHelp = {
+    "NAME",
+    "    setmissval, setctomiss, setmisstoc, setrtomiss, setvrange, setmisstonn, ",
+    "    setmisstodis - Set missing value",
+    "",
+    "SYNOPSIS",
+    "    setmissval,newmiss  infile outfile",
+    "    setctomiss,c  infile outfile",
+    "    setmisstoc,c  infile outfile",
+    "    setrtomiss,rmin,rmax  infile outfile",
+    "    setvrange,rmin,rmax  infile outfile",
+    "    setmisstonn  infile outfile",
+    "    setmisstodis[,neighbors]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module sets part of a field to missing value or missing values",
+    "    to a constant value. Which part of the field is set depends on the ",
+    "    chosen operator.",
+    "",
+    "OPERATORS",
+    "    setmissval    Set a new missing value",
+    "                           / newmiss   if i(t,x) EQ miss",
+    "                  o(t,x) = ",
+    "                           \\ i(t,x)    if i(t,x) NE miss",
+    "    setctomiss    Set constant to missing value",
+    "                           / miss   if i(t,x) EQ c",
+    "                  o(t,x) = ",
+    "                           \\ i(t,x) if i(t,x) NE c",
+    "    setmisstoc    Set missing value to constant",
+    "                           / c      if i(t,x) EQ miss",
+    "                  o(t,x) = ",
+    "                           \\ i(t,x) if i(t,x) NE miss",
+    "    setrtomiss    Set range to missing value",
+    "                           / miss   if i(t,x) GE rmin AND i(t,x) LE rmax",
+    "                  o(t,x) = ",
+    "                           \\ i(t,x) if i(t,x) LT rmin OR  i(t,x) GT rmax",
+    "    setvrange     Set valid range",
+    "                           / miss   if i(t,x) LT rmin OR  i(t,x) GT rmax",
+    "                  o(t,x) = ",
+    "                           \\ i(t,x) if i(t,x) GE rmin AND i(t,x) LE rmax",
+    "    setmisstonn   Set missing value to nearest neighbor",
+    "                  Set all missing values to the nearest non missing value.",
+    "                           / i(t,y) if i(t,x) EQ miss AND i(t,y) NE miss",
+    "                  o(t,x) = ",
+    "                           \\ i(t,x) if i(t,x) NE miss",
+    "    setmisstodis  Set missing value to distance-weighted average",
+    "                  Set all missing values to the distance-weighted average of the nearest non missing values.",
+    "                  The default number of nearest neighbors is 4.",
+    "",
+    "PARAMETER",
+    "    neighbors  INTEGER  Number of nearest neighbors",
+    "    newmiss    FLOAT    New missing value",
+    "    c          FLOAT    Constant",
+    "    rmin       FLOAT    Lower bound",
+    "    rmax       FLOAT    Upper bound",
+};
+
+const CdoHelp VertfillmissHelp = {
+    "NAME",
+    "    vertfillmiss - Vertical filling of missing values",
+    "",
+    "SYNOPSIS",
+    "    vertfillmiss[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator fills in vertical missing values.",
+    "    The method parameter can be used to select the filling method.",
+    "    The default method=nearest fills missing values with the nearest neighbor value.",
+    "    Other options are forward and backward to fill missing values by forward or backward propagation of values.",
+    "    Use the limit parameter to set the maximum number of consecutive missing values to fill and max_gaps to set the maximum number of gaps to fill.",
+    "",
+    "PARAMETER",
+    "    method    STRING   Fill method [nearest|linear|forward|backward] (default: nearest)",
+    "    limit     INTEGER  The maximum number of consecutive missing values to fill (default: all)",
+    "    max_gaps  INTEGER  The maximum number of gaps to fill (default: all)",
+};
+
+const CdoHelp TimfillmissHelp = {
+    "NAME",
+    "    timfillmiss - Temporal filling of missing values",
+    "",
+    "SYNOPSIS",
+    "    timfillmiss[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator fills in temporally missing values.",
+    "    The method parameter can be used to select the filling method.",
+    "    The default method=nearest fills missing values with the nearest neighbor value.",
+    "    Other options are forward and backward to fill missing values by forward or backward propagation of values.",
+    "    Use the limit parameter to set the maximum number of consecutive missing values to fill and max_gaps to set the maximum number of gaps to fill.",
+    "",
+    "PARAMETER",
+    "    method    STRING   Fill method [nearest|linear|forward|backward] (default: nearest)",
+    "    limit     INTEGER  The maximum number of consecutive missing values to fill (default: all)",
+    "    max_gaps  INTEGER  The maximum number of gaps to fill (default: all)",
+};
+
+const CdoHelp SetgridcellHelp = {
+    "NAME",
+    "    setgridcell - Set the value of a grid cell",
+    "",
+    "SYNOPSIS",
+    "    setgridcell,parameter  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator sets the value of the selected grid cells. The grid cells can be selected by a comma-separated list of grid cell indices",
+    "    or a mask. The mask is read from a data file, which may contain only one field. If no grid cells are selected, all values are set.",
+    "",
+    "PARAMETER",
+    "    value  FLOAT    Value of the grid cell",
+    "    cell   INTEGER  Comma-separated list of grid cell indices",
+    "    mask   STRING   Name of the data file which contains the mask",
+};
+
+const CdoHelp ExprHelp = {
+    "NAME",
+    "    expr, exprf, aexpr, aexprf - Evaluate expressions",
+    "",
+    "SYNOPSIS",
+    "    expr,instr  infile outfile",
+    "    exprf,filename  infile outfile",
+    "    aexpr,instr  infile outfile",
+    "    aexprf,filename  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module arithmetically processes every timestep of the input dataset.",
+    "    Each individual assignment statement have to end with a semi-colon.",
+    "    The special key _ALL_ is used as a template. A statement with a template is replaced for all variable names.",
+    "    Unlike regular variables, temporary variables are never written to the output stream.",
+    "    To define a temporary variable simply prefix the variable name with an underscore (e.g. _varname)",
+    "    when the variable is declared.",
+    "    ",
+    "    The following operators are supported:",
+    "    ",
+    "     Operator   & Meaning             & Example   & Result ",
+    "         =      & assignment          & x = y     & Assigns y to x",
+    "         +      & addition            & x + y     & Sum of x and y",
+    "         -      & subtraction         & x - y     & Difference of x and y    ",
+    "         *      & multiplication      & x * y     & Product of x and y ",
+    "         /      & division            & x / y     & Quotient of x and y",
+    "         ^      & exponentiation      & x ^y      & Exponentiates x with y ",
+    "         ==     & equal to            & x == y    &  1, if x equal to y; else 0",
+    "         !=     & not equal to        & x != y    &  1, if x not equal to y; else 0",
+    "         >      & greater than        & x > y     &  1, if x greater than y; else 0",
+    "         <      & less than           & x < y     &  1, if x less than y; else 0",
+    "         >=     & greater equal       & x >= y    &  1, if x greater equal y; else 0",
+    "         <=     & less equal          & x <= y    &  1, if x less equal y; else 0",
+    "         <=>    & less equal greater  & x <=> y   & -1, if x less y; 1, if x greater y; else 0 ",
+    "         &&     & logical AND         & x && y    &  1, if x and y not equal 0; else 0",
+    "         ||     & logical OR          & x || y    &  1, if x or y not equal 0; else 0",
+    "         !      & logical NOT         & !x        &  1, if x equal 0; else 0",
+    "         ?:     & ternary conditional & x ? y : z & y, if x not equal 0, else z ",
+    "    ",
+    "    The following functions are supported:",
+    "    ",
+    "    Math intrinsics:",
+    "    ",
+    "    abs(x)      "    "    Absolute value of x",
+    "    floor(x)    "    "    Round to largest integral value not greater than x",
+    "    ceil(x)     "    "    Round to smallest integral value not less than x",
+    "    float(x)    "    "    32-bit float value of x",
+    "    int(x)      "    "    Integer value of x",
+    "    nint(x)     "    "    Nearest integer value of x",
+    "    sqr(x)      "    "    Square of x",
+    "    sqrt(x)     "    "    Square Root of x",
+    "    exp(x)      "    "    Exponential of x",
+    "    ln(x)       "    "    Natural logarithm of x",
+    "    log10(x)    "    "    Base 10 logarithm of x",
+    "    sin(x)      "    "    Sine of x, where x is specified in radians",
+    "    cos(x)      "    "    Cosine of x, where x is specified in radians",
+    "    tan(x)      "    "    Tangent of x, where x is specified in radians",
+    "    asin(x)     "    "    Arc-sine of x, where x is specified in radians",
+    "    acos(x)     "    "    Arc-cosine of x, where x is specified in radians",
+    "    atan(x)     "    "    Arc-tangent of x, where x is specified in radians",
+    "    sinh(x)     "    "    Hyperbolic sine of x, where x is specified in radians",
+    "    cosh(x)     "    "    Hyperbolic cosine of x, where x is specified in radians",
+    "    tanh(x)     "    "    Hyperbolic tangent of x, where x is specified in radians",
+    "    asinh(x)    "    "    Inverse hyperbolic sine of x, where x is specified in radians",
+    "    acosh(x)    "    "    Inverse hyperbolic cosine of x, where x is specified in radians",
+    "    atanh(x)    "    "    Inverse hyperbolic tangent of x, where x is specified in radians",
+    "    rad(x)      "    "    Convert x from degrees to radians",
+    "    deg(x)      "    "    Convert x from radians to degrees",
+    "    rand(x)     "    "    Replace x by pseudo-random numbers in the range of 0 to 1 ",
+    "    isMissval(x)"    "    Returns 1 where x is missing",
+    "    ",
+    "    mod(x,y)    "    "    Floating-point remainder of x/ y",
+    "    min(x,y)    "    "    Minimum value of x and y",
+    "    max(x,y)    "    "    Maximum value of x and y",
+    "    pow(x,y)    "    "    Power function",
+    "    hypot(x,y)  "    "    Euclidean distance function, sqrt(x*x + y*y)",
+    "    atan2(x,y)  "    "    Arc tangent function of y/x, using signs to determine quadrants ",
+    "    ",
+    "    Coordinates:",
+    "    ",
+    "    clon(x)      "    "    Longitude coordinate of x (available only if x has geographical coordinates)",
+    "    clat(x)      "    "    Latitude coordinate of x (available only if x has geographical coordinates)",
+    "    gridarea(x)  "    "    Grid cell area of x (available only if x has geographical coordinates)",
+    "    gridindex(x) "    "    Grid cell indices of x",
+    "    clev(x)      "    "    Level coordinate of x (0, if x is a 2D surface variable)",
+    "    clevidx(x)   "    "    Level index of x (0, if x is a 2D surface variable)",
+    "    cthickness(x)"    "    Layer thickness, upper minus lower level bound of x (1, if level bounds are missing)",
+    "    ctimestep()  "    "    Timestep number (1 to N)",
+    "    cdate()      "    "    Verification date as YYYYMMDD",
+    "    ctime()      "    "    Verification time as HHMMSS.millisecond",
+    "    cdeltat()    "    "    Difference between current and last timestep in seconds",
+    "    cday()       "    "    Day as DD",
+    "    cmonth()     "    "    Month as MM",
+    "    cyear()      "    "    Year as YYYY",
+    "    csecond()    "    "    Second as SS.millisecond",
+    "    cminute()    "    "    Minute as MM",
+    "    chour()      "    "    Hour as HH",
+    "    ",
+    "    Constants:",
+    "    ",
+    "    ngp(x)    "    "    Number of horizontal grid points",
+    "    nlev(x)   "    "    Number of vertical levels",
+    "    size(x)   "    "    Total number of elements (ngp(x)*nlev(x))",
+    "    missval(x)"    "    Returns the missing value of variable x",
+    "    ",
+    "    Statistical values over a field:",
+    "    ",
+    "    fldmin(x), fldmax(x), fldrange(x), fldsum(x), fldmean(x), fldavg(x), fldstd(x), fldstd1(x),",
+    "    fldvar(x), fldvar1(x), fldskew(x), fldkurt(x), fldmedian(x)",
+    "    ",
+    "    Zonal statistical values for regular 2D grids:",
+    "    ",
+    "    zonmin(x), zonmax(x), zonrange(x), zonsum(x), zonmean(x), zonavg(x), zonstd(x), zonstd1(x),",
+    "    zonvar(x), zonvar1(x), zonskew(x), zonkurt(x), zonmedian(x)",
+    "    ",
+    "    Vertical statistical values:",
+    "    ",
+    "    vertmin(x), vertmax(x), vertrange(x), vertsum(x), vertmean(x), vertavg(x), vertstd(x), vertstd1(x),",
+    "    vertvar(x), vertvar1(x)",
+    "    ",
+    "    Miscellaneous:",
+    "    ",
+    "    sellevel(x,k)          "    "    Select level k of variable x",
+    "    sellevidx(x,k)         "    "    Select level index k of variable x",
+    "    sellevelrange(x,k1,k2) "    "    Select all levels of variable x in the range k1 to k2",
+    "    sellevidxrange(x,k1,k2)"    "    Select all level indices of variable x in the range k1 to k2",
+    "    remove(x)              "    "    Remove variable x from output stream",
+    "    ",
+    "",
+    "OPERATORS",
+    "    expr    Evaluate expressions",
+    "            The processing instructions are read from the parameter.",
+    "    exprf   Evaluate expressions script",
+    "            Contrary to expr the processing instructions are read from a file.",
+    "    aexpr   Evaluate expressions and append results",
+    "            Same as expr, but keep input variables and append results",
+    "    aexprf  Evaluate expression script and append results",
+    "            Same as exprf, but keep input variables and append results",
+    "",
+    "PARAMETER",
+    "    instr     STRING  Processing instructions (need to be 'quoted' in most cases)",
+    "    filename  STRING  File with processing instructions",
+    "",
+    "NOTE",
+    "    If the input stream contains duplicate entries of the same variable name then the last one is used.",
+};
+
+const CdoHelp MathHelp = {
+    "NAME",
+    "    abs, int, nint, pow, sqr, sqrt, exp, ln, log10, sin, cos, tan, asin, acos, ",
+    "    atan, reci, not - Mathematical functions",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains some standard mathematical functions.",
+    "    All trigonometric functions calculate with radians.",
+    "",
+    "OPERATORS",
+    "    abs    Absolute value",
+    "           o(t,x) = abs(i(t,x))",
+    "    int    Integer value",
+    "           o(t,x) = int(i(t,x))",
+    "    nint   Nearest integer value",
+    "           o(t,x) = nint(i(t,x))",
+    "    pow    Power",
+    "           o(t,x) = i(t,x)^y",
+    "    sqr    Square",
+    "           o(t,x) = i(t,x)^2",
+    "    sqrt   Square root",
+    "           o(t,x) = sqrt(i(t,x))",
+    "    exp    Exponential",
+    "           o(t,x) = e^i(t,x)",
+    "    ln     Natural logarithm",
+    "           o(t,x) = ln(i(t,x))",
+    "    log10  Base 10 logarithm",
+    "           o(t,x) = log10(i(t,x))",
+    "    sin    Sine",
+    "           o(t,x) = sin(i(t,x))",
+    "    cos    Cosine",
+    "           o(t,x) = cos(i(t,x))",
+    "    tan    Tangent",
+    "           o(t,x) = tan(i(t,x))",
+    "    asin   Arc sine",
+    "           o(t,x) = asin(i(t,x))",
+    "    acos   Arc cosine",
+    "           o(t,x) = acos(i(t,x))",
+    "    atan   Arc tangent",
+    "           o(t,x) = atan(i(t,x))",
+    "    reci   Reciprocal value",
+    "           o(t,x) = 1 / i(t,x)",
+    "    not    Logical NOT",
+    "           o(t,x) = 1, if x equal 0; else 0",
+};
+
+const CdoHelp ArithcHelp = {
+    "NAME",
+    "    addc, subc, mulc, divc, minc, maxc - Arithmetic with a constant",
+    "",
+    "SYNOPSIS",
+    "    <operator>,c  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic with all field elements of a dataset and ",
+    "    a constant. The fields in outfile inherit the meta data from infile.",
+    "",
+    "OPERATORS",
+    "    addc  Add a constant",
+    "          o(t,x) = i(t,x) + c",
+    "    subc  Subtract a constant",
+    "          o(t,x) = i(t,x) - c",
+    "    mulc  Multiply with a constant",
+    "          o(t,x) = i(t,x) * c",
+    "    divc  Divide by a constant",
+    "          o(t,x) = i(t,x) / c",
+    "    minc  Minimum of a field and a constant",
+    "          o(t,x) = min(i(t,x), c)",
+    "    maxc  Maximum of a field and a constant",
+    "          o(t,x) = max(i(t,x), c)",
+    "",
+    "PARAMETER",
+    "    c  FLOAT  Constant",
+};
+
+const CdoHelp ArithHelp = {
+    "NAME",
+    "    add, sub, mul, div, min, max, atan2 - Arithmetic on two datasets",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of two datasets.",
+    "    The number of fields in infile1 should be the same as in infile2.",
+    "    The fields in outfile inherit the meta data from infile1.",
+    "    All operators in this module simply process one field after the other from the two input files.",
+    "    Neither the order of the variables nor the date is checked.",
+    "    One of the input files can contain only one timestep or one variable.",
+    "",
+    "OPERATORS",
+    "    add    Add two fields",
+    "           o(t,x) = i_1(t,x) + i_2(t,x)",
+    "    sub    Subtract two fields",
+    "           o(t,x) = i_1(t,x) - i_2(t,x)",
+    "    mul    Multiply two fields",
+    "           o(t,x) = i_1(t,x) * i_2(t,x)",
+    "    div    Divide two fields",
+    "           o(t,x) = i_1(t,x) / i_2(t,x)",
+    "    min    Minimum of two fields",
+    "           o(t,x) = min(i_1(t,x), i_2(t,x))",
+    "    max    Maximum of two fields",
+    "           o(t,x) = max(i_1(t,x), i_2(t,x))",
+    "    atan2  Arc tangent of two fields",
+    "           The atan2 operator calculates the arc tangent of two fields. The result is",
+    "           in radians, which is between -PI and PI (inclusive).",
+    "           ",
+    "           o(t,x) = atan2(i_1(t,x), i_2(t,x))",
+};
+
+const CdoHelp DayarithHelp = {
+    "NAME",
+    "    dayadd, daysub, daymul, daydiv - Daily arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep with the same day, month and year.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same day, month and year is used.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module DAYSTAT.",
+    "",
+    "OPERATORS",
+    "    dayadd  Add daily time series",
+    "            Adds a time series and a daily time series.",
+    "    daysub  Subtract daily time series",
+    "            Subtracts a time series and a daily time series.",
+    "    daymul  Multiply daily time series",
+    "            Multiplies a time series and a daily time series.",
+    "    daydiv  Divide daily time series",
+    "            Divides a time series and a daily time series.",
+};
+
+const CdoHelp MonarithHelp = {
+    "NAME",
+    "    monadd, monsub, monmul, mondiv - Monthly arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep with the same month and year.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same month and year is used.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module MONSTAT.",
+    "",
+    "OPERATORS",
+    "    monadd  Add monthly time series",
+    "            Adds a time series and a monthly time series.",
+    "    monsub  Subtract monthly time series",
+    "            Subtracts a time series and a monthly time series.",
+    "    monmul  Multiply monthly time series",
+    "            Multiplies a time series and a monthly time series.",
+    "    mondiv  Divide monthly time series",
+    "            Divides a time series and a monthly time series.",
+};
+
+const CdoHelp YeararithHelp = {
+    "NAME",
+    "    yearadd, yearsub, yearmul, yeardiv - Yearly arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep",
+    "    with the same year. For each field in infile1 the corresponding",
+    "    field of the timestep in infile2 with the same year is used.",
+    "    The header information in infile1 have to be the same as in infile2.",
+    "    Usually infile2 is generated by an operator of the module YEARSTAT.",
+    "",
+    "OPERATORS",
+    "    yearadd  Add yearly time series",
+    "             Adds a time series and a yearly time series.",
+    "    yearsub  Subtract yearly time series",
+    "             Subtracts a time series and a yearly time series.",
+    "    yearmul  Multiply yearly time series",
+    "             Multiplies a time series and a yearly time series.",
+    "    yeardiv  Divide yearly time series",
+    "             Divides a time series and a yearly time series.",
+};
+
+const CdoHelp YhourarithHelp = {
+    "NAME",
+    "    yhouradd, yhoursub, yhourmul, yhourdiv - Multi-year hourly arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep with the same hour and day of year.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same hour and day of year is used.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module YHOURSTAT.",
+    "",
+    "OPERATORS",
+    "    yhouradd  Add multi-year hourly time series",
+    "              Adds a time series and a multi-year hourly time series.",
+    "    yhoursub  Subtract multi-year hourly time series",
+    "              Subtracts a time series and a multi-year hourly time series.",
+    "    yhourmul  Multiply multi-year hourly time series",
+    "              Multiplies a time series and a multi-year hourly time series.",
+    "    yhourdiv  Divide multi-year hourly time series",
+    "              Divides a time series and a multi-year hourly time series.",
+};
+
+const CdoHelp YdayarithHelp = {
+    "NAME",
+    "    ydayadd, ydaysub, ydaymul, ydaydiv - Multi-year daily arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep with the same day of year.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same day of year is used.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module YDAYSTAT.",
+    "",
+    "OPERATORS",
+    "    ydayadd  Add multi-year daily time series",
+    "             Adds a time series and a multi-year daily time series.",
+    "    ydaysub  Subtract multi-year daily time series",
+    "             Subtracts a time series and a multi-year daily time series.",
+    "    ydaymul  Multiply multi-year daily time series",
+    "             Multiplies a time series and a multi-year daily time series.",
+    "    ydaydiv  Divide multi-year daily time series",
+    "             Divides a time series and a multi-year daily time series.",
+};
+
+const CdoHelp YmonarithHelp = {
+    "NAME",
+    "    ymonadd, ymonsub, ymonmul, ymondiv - Multi-year monthly arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep with the same month of year.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same month of year is used.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module YMONSTAT.",
+    "",
+    "OPERATORS",
+    "    ymonadd  Add multi-year monthly time series",
+    "             Adds a time series and a multi-year monthly time series.",
+    "    ymonsub  Subtract multi-year monthly time series",
+    "             Subtracts a time series and a multi-year monthly time series.",
+    "    ymonmul  Multiply multi-year monthly time series",
+    "             Multiplies a time series with a multi-year monthly time series.",
+    "    ymondiv  Divide multi-year monthly time series",
+    "             Divides a time series by a multi-year monthly time series.",
+};
+
+const CdoHelp YseasarithHelp = {
+    "NAME",
+    "    yseasadd, yseassub, yseasmul, yseasdiv - Multi-year seasonal arithmetic",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs simple arithmetic of a time series and one timestep with the same season.",
+    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same season is used.",
+    "    The input files need to have the same structure with the same variables.",
+    "    Usually infile2 is generated by an operator of the module YSEASSTAT.",
+    "",
+    "OPERATORS",
+    "    yseasadd  Add multi-year seasonal time series",
+    "              Adds a time series and a multi-year seasonal time series.",
+    "    yseassub  Subtract multi-year seasonal time series",
+    "              Subtracts a time series and a multi-year seasonal time series.",
+    "    yseasmul  Multiply multi-year seasonal time series",
+    "              Multiplies a time series and a multi-year seasonal time series.",
+    "    yseasdiv  Divide multi-year seasonal time series",
+    "              Divides a time series and a multi-year seasonal time series.",
+};
+
+const CdoHelp ArithdaysHelp = {
+    "NAME",
+    "    muldpm, divdpm, muldpy, divdpy - Arithmetic with days",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module multiplies or divides each timestep of a dataset with the corresponding",
+    "    days per month or days per year. The result of these functions depends on the used",
+    "    calendar of the input data.",
+    "",
+    "OPERATORS",
+    "    muldpm  Multiply with days per month",
+    "            o(t,x) = i(t,x) * days_per_month",
+    "    divdpm  Divide by days per month",
+    "            o(t,x) = i(t,x) / days_per_month",
+    "    muldpy  Multiply with days per year",
+    "            o(t,x) = i(t,x) * days_per_year",
+    "    divdpy  Divide by days per year",
+    "            o(t,x) = i(t,x) / days_per_year",
+};
+
+const CdoHelp ArithlatHelp = {
+    "NAME",
+    "    mulcoslat, divcoslat - Arithmetic with latitude",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module multiplies or divides each field element with the cosine of the latitude.",
+    "",
+    "OPERATORS",
+    "    mulcoslat  Multiply with the cosine of the latitude",
+    "               o(t,x) = i(t,x) * cos(latitude(x))",
+    "    divcoslat  Divide by cosine of the latitude",
+    "               o(t,x) = i(t,x) / cos(latitude(x))",
+};
+
+const CdoHelp TimcumsumHelp = {
+    "NAME",
+    "    timcumsum - Cumulative sum over all timesteps",
+    "",
+    "SYNOPSIS",
+    "    timcumsum  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    The timcumsum operator calculates the cumulative sum over all timesteps.",
+    "    Missing values are treated as numeric zero when summing.",
+    "    ",
+    "    o(t,x) = sum{i(t',x), 0<t'<=t}",
+};
+
+const CdoHelp ConsecstatHelp = {
+    "NAME",
+    "    consecsum, consects - Consecute timestep periods",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes periods over all timesteps in infile where a",
+    "    certain property is valid. The property can be chosen by creating a mask from",
+    "    the original data, which is the expected input format for operators of this",
+    "    module. Depending on the operator full information about each period or",
+    "    just its length and ending date are computed.",
+    "",
+    "OPERATORS",
+    "    consecsum  Consecutive Sum",
+    "               This operator computes periods of consecutive timesteps similar to a",
+    "               runsum, but periods are finished, when the mask value is 0. That way",
+    "               multiple periods can be found. Timesteps from the input are preserved. Missing",
+    "               values are handled like 0, i.e. finish periods of consecutive timesteps.",
+    "    consects   Consecutive Timesteps",
+    "               In contrast to the operator above consects only computes the length of each",
+    "               period together with its last timestep. To be able to perform statistical",
+    "               analysis like min, max or mean, everything else is set to missing value.",
+};
+
+const CdoHelp VarsstatHelp = {
+    "NAME",
+    "    varsmin, varsmax, varsrange, varssum, varsmean, varsavg, varsstd, varsstd1, ",
+    "    varsvar, varsvar1 - Statistical values over all variables",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over all variables for each timestep.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance or",
+    "    standard deviation is written to outfile.",
+    "    All input variables need to have the same gridsize and the same number of levels.",
+    "",
+    "OPERATORS",
+    "    varsmin    Variables minimum",
+    "               For every timestep the minimum over all variables is computed.",
+    "    varsmax    Variables maximum",
+    "               For every timestep the maximum over all variables is computed.",
+    "    varsrange  Variables range",
+    "               For every timestep the range over all variables is computed.",
+    "    varssum    Variables sum",
+    "               For every timestep the sum over all variables is computed.",
+    "    varsmean   Variables mean",
+    "               For every timestep the mean over all variables is computed.",
+    "    varsavg    Variables average",
+    "               For every timestep the average over all variables is computed.",
+    "    varsstd    Variables standard deviation",
+    "               For every timestep the standard deviation over all variables is computed. Normalize by n.",
+    "    varsstd1   Variables standard deviation (n-1)",
+    "               For every timestep the standard deviation over all variables is computed. Normalize by (n-1).",
+    "    varsvar    Variables variance",
+    "               For every timestep the variance over all variables is computed. Normalize by n.",
+    "    varsvar1   Variables variance (n-1)",
+    "               For every timestep the variance over all variables is computed. Normalize by (n-1).",
+};
+
+const CdoHelp EnsstatHelp = {
+    "NAME",
+    "    ensmin, ensmax, ensrange, enssum, ensmean, ensavg, ensstd, ensstd1, ensvar, ",
+    "    ensvar1, ensskew, enskurt, ensmedian, enspctl - ",
+    "    Statistical values over an ensemble",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infiles outfile",
+    "    enspctl,p  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over an ensemble of input files.",
+    "    Depending on the chosen operator, the minimum, maximum, range, sum, average, standard deviation, variance,",
+    "    skewness, kurtosis, median or a certain percentile over all input files is written to outfile.",
+    "    All input files need to have the same structure with the same variables.",
+    "    The date information of a timestep in outfile is the date of the first input file.",
+    "",
+    "OPERATORS",
+    "    ensmin     Ensemble minimum",
+    "               o(t,x) = min{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensmax     Ensemble maximum",
+    "               o(t,x) = max{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensrange   Ensemble range",
+    "               o(t,x) = range{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    enssum     Ensemble sum",
+    "               o(t,x) = sum{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensmean    Ensemble mean",
+    "               o(t,x) = mean{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensavg     Ensemble average",
+    "               o(t,x) = avg{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensstd     Ensemble standard deviation",
+    "               Normalize by n.",
+    "               ",
+    "               o(t,x) = std{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensstd1    Ensemble standard deviation (n-1)",
+    "               Normalize by (n-1).",
+    "               ",
+    "               o(t,x) = std1{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensvar     Ensemble variance",
+    "               Normalize by n.",
+    "               ",
+    "               o(t,x) = var{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensvar1    Ensemble variance (n-1)",
+    "               Normalize by (n-1).",
+    "               ",
+    "               o(t,x) = var1{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensskew    Ensemble skewness",
+    "               o(t,x) = skew{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    enskurt    Ensemble kurtosis",
+    "               o(t,x) = kurt{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    ensmedian  Ensemble median",
+    "               o(t,x) = median{i1(t,x), i2(t,x), ..., in(t,x)}",
+    "    enspctl    Ensemble percentiles",
+    "               o(t,x) = pth percentile {i1(t,x), i2(t,x), ..., in(t,x)}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "NOTE",
+    "    Operators of this module need to open all input files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp Ensstat2Help = {
+    "NAME",
+    "    ensrkhistspace, ensrkhisttime, ensroc - Statistical values over an ensemble",
+    "",
+    "SYNOPSIS",
+    "    <operator>  obsfile ensfiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over the ensemble of ensfiles using",
+    "    obsfile as a reference. Depending on the operator a ranked Histogram or ",
+    "    a roc-curve over all Ensembles ensfiles",
+    "    with reference to obsfile is written to outfile. ",
+    "    The date and grid information of a timestep in outfile is the date of the ",
+    "    first input file. Thus all input files are required to have the same structure in ",
+    "    terms of the gridsize, variable definitions and number of timesteps. ",
+    "    ",
+    "    All Operators in this module use obsfile as the reference (for instance ",
+    "    an observation) whereas ensfiles are understood as an ensemble consisting ",
+    "    of n (where n is the number of ensfiles) members. ",
+    "    ",
+    "    The operators ensrkhistspace and ensrkhisttime compute Ranked Histograms. ",
+    "    Therefor the vertical axis is utilized as the Histogram axis, which prohibits",
+    "    the use of files containing more than one level. The histogram axis has ",
+    "    nensfiles+1 bins with level 0 containing for each grid point the number of ",
+    "    observations being smaller as all ensembles and level nensfiles+1 indicating",
+    "    the number of observations being larger than all ensembles. ",
+    "    ",
+    "    ensrkhistspace computes a ranked histogram at each timestep reducing each ",
+    "    horizontal grid to a 1x1 grid and keeping the time axis as in obsfile. ",
+    "    Contrary ensrkhistspace  computes a histogram at each grid point keeping the ",
+    "    horizontal grid for each variable and reducing the time-axis. The time information",
+    "    is that from the last timestep in obsfile. ",
+    "",
+    "OPERATORS",
+    "    ensrkhistspace  Ranked Histogram averaged over time",
+    "    ensrkhisttime   Ranked Histogram averaged over space",
+    "    ensroc          Ensemble Receiver Operating characteristics",
+};
+
+const CdoHelp EnsvalHelp = {
+    "NAME",
+    "    enscrps, ensbrs - Ensemble validation tools",
+    "",
+    "SYNOPSIS",
+    "    enscrps  rfile infiles outfilebase",
+    "    ensbrs,x  rfile infiles outfilebase",
+    "",
+    "DESCRIPTION",
+    "    This module computes ensemble validation scores and their decomposition such as ",
+    "    the Brier and cumulative ranked probability score (CRPS). ",
+    "    The first file is used as a reference it can be a climatology, observation or ",
+    "    reanalysis against which the skill of the ensembles given in infiles is measured. ",
+    "    Depending on the operator a number of output files is generated each containing ",
+    "    the skill score and its decomposition corresponding to the operator. ",
+    "    The output is averaged  over horizontal fields using appropriate weights ",
+    "    for each level and timestep in rfile. ",
+    "    ",
+    "    All input files need to have the same structure with the same variables.",
+    "    The date information of a timestep in outfile is the date of the first input file.",
+    "    The output files are named as ",
+    "    <outfilebase>.<type>.<filesuffix> where <type> depends on the ",
+    "    operator and <filesuffix> is determined from the output file type. ",
+    "    There are three output files for operator enscrps and four output files ",
+    "    for operator ensbrs.",
+    "    ",
+    "    The CRPS and its decomposition into Reliability and the potential ",
+    "    CRPS are calculated by an appropriate averaging over the field ",
+    "    members (note, that the CRPS does *not* average linearly). ",
+    "    In the three output files ",
+    "    <type> has the following meaning:",
+    "    crps for the CRPS, reli for the reliability ",
+    "    and crpspot for the potential crps. The relation ",
+    "    CRPS = CRPS_{pot} + RELI",
+    "    holds. 	  ",
+    "    ",
+    "    The Brier score of the Ensemble given by infiles with respect to the ",
+    "    reference given in rfile and the threshold x is calculated. ",
+    "    In the four output files <type> has the following meaning: ",
+    "    brs for the Brier score wrt threshold  x; ",
+    "    brsreli for the Brier score reliability wrt threshold x;",
+    "    brsreso for the Brier score resolution wrt threshold x;",
+    "    brsunct for the Brier score uncertainty wrt threshold x.",
+    "    In analogy to the CRPS the following relation holds:",
+    "    BRS(x) = RELI(x)-RESO(x)+ UNCT(x).",
+    "    ",
+    "    The implementation of the decomposition of the CRPS and Brier Score follows ",
+    "      Hans Hersbach (2000): Decomposition of the Continuous Ranked Probability ",
+    "      Score for Ensemble Prediction Systems, in: Weather and Forecasting (15) ",
+    "      pp. 559-570. ",
+    "    ",
+    "    The CRPS code decomposition has been verified against the CRAN - ensemble ",
+    "    validation package from R. Differences occur when grid-cell area is not ",
+    "    uniform as the implementation in R does not account for that. ",
+    "    ",
+    "",
+    "OPERATORS",
+    "    enscrps  Ensemble CRPS and decomposition",
+    "    ensbrs   Ensemble Brier score",
+    "             Ensemble Brier Score and Decomposition",
+};
+
+const CdoHelp FldstatHelp = {
+    "NAME",
+    "    fldmin, fldmax, fldrange, fldsum, fldint, fldmean, fldavg, fldstd, fldstd1, ",
+    "    fldvar, fldvar1, fldskew, fldkurt, fldmedian, fldcount, fldpctl - ",
+    "    Statistical values over a field",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "    fldint,weights  infile outfile",
+    "    fldmean,weights  infile outfile",
+    "    fldavg,weights  infile outfile",
+    "    fldstd,weights  infile outfile",
+    "    fldstd1,weights  infile outfile",
+    "    fldvar,weights  infile outfile",
+    "    fldvar1,weights  infile outfile",
+    "    fldpctl,p  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of all input fields. A field is a horizontal layer of a data variable.",
+    "    Depending on the chosen operator, the minimum, maximum, range, sum, integral, average, standard deviation, variance,",
+    "    skewness, kurtosis, median or a certain percentile of the field is written to outfile.",
+    "",
+    "OPERATORS",
+    "    fldmin     Field minimum",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = min{i(t,x'), x_1<x'<=x_n}",
+    "    fldmax     Field maximum",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = max{i(t,x'), x_1<x'<=x_n}",
+    "    fldrange   Field range",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = range{i(t,x'), x_1<x'<=x_n}",
+    "    fldsum     Field sum",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = sum{i(t,x'), x_1<x'<=x_n}",
+    "    fldint     Field integral",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = sum{i(t,x')*cellarea(x'), x_1<x'<=x_n}",
+    "    fldmean    Field mean",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = mean{i(t,x'), x_1<x'<=x_n}",
+    "               weighted by area weights obtained by the input field.",
+    "    fldavg     Field average",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = avg{i(t,x'), x_1<x'<=x_n}",
+    "               weighted by area weights obtained by the input field.",
+    "    fldstd     Field standard deviation",
+    "               Normalize by n. For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = std{i(t,x'), x_1<x'<=x_n}",
+    "               weighted by area weights obtained by the input field.",
+    "    fldstd1    Field standard deviation (n-1)",
+    "               Normalize by (n-1). For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = std1{i(t,x'), x_1<x'<=x_n}",
+    "               weighted by area weights obtained by the input field.",
+    "    fldvar     Field variance",
+    "               Normalize by n. For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = var{i(t,x'), x_1<x'<=x_n}",
+    "               weighted by area weights obtained by the input field.",
+    "    fldvar1    Field variance (n-1)",
+    "               Normalize by (n-1). For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = var1{i(t,x'), x_1<x'<=x_n}",
+    "               weighted by area weights obtained by the input field.",
+    "    fldskew    Field skewness",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = skew{i(t,x'), x_1<x'<=x_n}",
+    "    fldkurt    Field kurtosis",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = kurt{i(t,x'), x_1<x'<=x_n}",
+    "    fldmedian  Field median",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = median{i(t,x'), x_1<x'<=x_n}",
+    "    fldcount   Field count",
+    "               Number of non-missing values of the field.",
+    "    fldpctl    Field percentiles",
+    "               For every gridpoint x_1, ..., x_n of the same field it is:",
+    "               ",
+    "               o(t,1) = pth percentile {i(t,x'), x_1<x'<=x_n}",
+    "",
+    "PARAMETER",
+    "    weights  BOOL   weights=FALSE disables weighting by grid cell area [default: weights=TRUE]",
+    "    p        FLOAT  Percentile number in {0, ..., 100}",
+};
+
+const CdoHelp ZonstatHelp = {
+    "NAME",
+    "    zonmin, zonmax, zonrange, zonsum, zonmean, zonavg, zonstd, zonstd1, zonvar, ",
+    "    zonvar1, zonskew, zonkurt, zonmedian, zonpctl - Zonal statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "    zonmean[,zonaldes]  infile outfile",
+    "    zonpctl,p  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes zonal statistical values of the input fields.",
+    "    Depending on the chosen operator, the zonal minimum, maximum, range, sum, average, standard deviation, variance,",
+    "    skewness, kurtosis, median or a certain percentile of the field is written to outfile.",
+    "    Operators of this module require all variables on the same regular lon/lat grid.",
+    "    Only the zonal mean (zonmean) can be calculated for data on an unstructured grid if the latitude bins are",
+    "    defined with the optional parameter zonaldes.",
+    "",
+    "OPERATORS",
+    "    zonmin     Zonal minimum",
+    "               For every latitude the minimum over all longitudes is computed.",
+    "    zonmax     Zonal maximum",
+    "               For every latitude the maximum over all longitudes is computed.",
+    "    zonrange   Zonal range",
+    "               For every latitude the range over all longitudes is computed.",
+    "    zonsum     Zonal sum",
+    "               For every latitude the sum over all longitudes is computed.",
+    "    zonmean    Zonal mean",
+    "               For every latitude the mean over all longitudes is computed.",
+    "               Use the optional parameter zonaldes for data on an unstructured grid.",
+    "    zonavg     Zonal average",
+    "               For every latitude the average over all longitudes is computed.",
+    "    zonstd     Zonal standard deviation",
+    "               For every latitude the standard deviation over all longitudes is computed. Normalize by n.",
+    "    zonstd1    Zonal standard deviation (n-1)",
+    "               For every latitude the standard deviation over all longitudes is computed. Normalize by (n-1). ",
+    "    zonvar     Zonal variance",
+    "               For every latitude the variance over all longitudes is computed. Normalize by n.",
+    "    zonvar1    Zonal variance (n-1)",
+    "               For every latitude the variance over all longitudes is computed. Normalize by (n-1).",
+    "    zonskew    Zonal skewness",
+    "               For every latitude the skewness over all longitudes is computed.",
+    "    zonkurt    Zonal kurtosis",
+    "               For every latitude the kurtosis over all longitudes is computed.",
+    "    zonmedian  Zonal median",
+    "               For every latitude the median over all longitudes is computed.",
+    "    zonpctl    Zonal percentiles",
+    "               For every latitude the pth percentile over all longitudes is computed.",
+    "",
+    "PARAMETER",
+    "    p         FLOAT   Percentile number in {0, ..., 100}",
+    "    zonaldes  STRING  Description of the zonal latitude bins needed for data on an unstructured grid. A predefined zonal description is zonal_<DY>. DY is the increment of the latitudes in degrees.",
+};
+
+const CdoHelp MerstatHelp = {
+    "NAME",
+    "    mermin, mermax, merrange, mersum, mermean, meravg, merstd, merstd1, mervar, ",
+    "    mervar1, merskew, merkurt, mermedian, merpctl - Meridional statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "    merpctl,p  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes meridional statistical values of the input fields.",
+    "    Depending on the chosen operator, the meridional minimum, maximum, range, sum, average, standard deviation, variance,",
+    "    skewness, kurtosis, median or a certain percentile of the field is written to outfile.",
+    "    Operators of this module require all variables on the same regular lon/lat grid.",
+    "",
+    "OPERATORS",
+    "    mermin     Meridional minimum",
+    "               For every longitude the minimum over all latitudes is computed.",
+    "    mermax     Meridional maximum",
+    "               For every longitude the maximum over all latitudes is computed.",
+    "    merrange   Meridional range",
+    "               For every longitude the range over all latitudes is computed.",
+    "    mersum     Meridional sum",
+    "               For every longitude the sum over all latitudes is computed.",
+    "    mermean    Meridional mean",
+    "               For every longitude the area weighted mean over all latitudes is computed.",
+    "    meravg     Meridional average",
+    "               For every longitude the area weighted average over all latitudes is computed.",
+    "    merstd     Meridional standard deviation",
+    "               For every longitude the standard deviation over all latitudes is computed. Normalize by n.",
+    "    merstd1    Meridional standard deviation (n-1)",
+    "               For every longitude the standard deviation over all latitudes is computed. Normalize by (n-1).",
+    "    mervar     Meridional variance",
+    "               For every longitude the variance over all latitudes is computed. Normalize by n.",
+    "    mervar1    Meridional variance (n-1)",
+    "               For every longitude the variance over all latitudes is computed. Normalize by (n-1).",
+    "    merskew    Meridional skewness",
+    "               For every longitude the skewness over all latitudes is computed.",
+    "    merkurt    Meridional kurtosis",
+    "               For every longitude the kurtosis over all latitudes is computed.",
+    "    mermedian  Meridional median",
+    "               For every longitude the median over all latitudes is computed.",
+    "    merpctl    Meridional percentiles",
+    "               For every longitude the pth percentile over all latitudes is computed.",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+};
+
+const CdoHelp GridboxstatHelp = {
+    "NAME",
+    "    gridboxmin, gridboxmax, gridboxrange, gridboxsum, gridboxmean, gridboxavg, ",
+    "    gridboxstd, gridboxstd1, gridboxvar, gridboxvar1, gridboxskew, gridboxkurt, ",
+    "    gridboxmedian - Statistical values over grid boxes",
+    "",
+    "SYNOPSIS",
+    "    <operator>,nx,ny  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over surrounding grid boxes.",
+    "    Depending on the chosen operator, the minimum, maximum, range, sum, average, standard deviation, variance,",
+    "    skewness, kurtosis or median of the neighboring grid boxes is written to outfile.",
+    "    All gridbox operators only work on quadrilateral curvilinear grids.",
+    "",
+    "OPERATORS",
+    "    gridboxmin     Gridbox minimum",
+    "                   Minimum value of the selected grid boxes.",
+    "    gridboxmax     Gridbox maximum",
+    "                   Maximum value of the selected grid boxes.",
+    "    gridboxrange   Gridbox range",
+    "                   Range (max-min value) of the selected grid boxes.",
+    "    gridboxsum     Gridbox sum",
+    "                   Sum of the selected grid boxes.",
+    "    gridboxmean    Gridbox mean",
+    "                   Mean of the selected grid boxes.",
+    "    gridboxavg     Gridbox average",
+    "                   Average of the selected grid boxes.",
+    "    gridboxstd     Gridbox standard deviation",
+    "                   Standard deviation of the selected grid boxes. Normalize by n.",
+    "    gridboxstd1    Gridbox standard deviation (n-1)",
+    "                   Standard deviation of the selected grid boxes. Normalize by (n-1).",
+    "    gridboxvar     Gridbox variance",
+    "                   Variance of the selected grid boxes. Normalize by n.",
+    "    gridboxvar1    Gridbox variance (n-1)",
+    "                   Variance of the selected grid boxes. Normalize by (n-1).",
+    "    gridboxskew    Gridbox skewness",
+    "                   Skewness of the selected grid boxes.",
+    "    gridboxkurt    Gridbox kurtosis",
+    "                   Kurtosis of the selected grid boxes.",
+    "    gridboxmedian  Gridbox median",
+    "                   Median of the selected grid boxes.",
+    "",
+    "PARAMETER",
+    "    nx  INTEGER  Number of grid boxes in x direction",
+    "    ny  INTEGER  Number of grid boxes in y direction",
+};
+
+const CdoHelp RemapstatHelp = {
+    "NAME",
+    "    remapmin, remapmax, remaprange, remapsum, remapmean, remapavg, remapstd, ",
+    "    remapstd1, remapvar, remapvar1, remapskew, remapkurt, remapmedian - ",
+    "    Remaps source points to target cells",
+    "",
+    "SYNOPSIS",
+    "    <operator>,grid  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module maps source points to target cells by calculating a statistical value from the source points.",
+    "    Each target cell contains the statistical value from all source points within that target cell.",
+    "    If there are no source points within a target cell, it gets a missing value.",
+    "    The target grid must be regular lon/lat or Gaussian.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance,",
+    "    standard deviation, skewness, kurtosis or median of source points is computed.",
+    "",
+    "OPERATORS",
+    "    remapmin     Remap minimum",
+    "                 Minimum value of the source points.",
+    "    remapmax     Remap maximum",
+    "                 Maximum value of the source points.",
+    "    remaprange   Remap range",
+    "                 Range (max-min value) of the source points.",
+    "    remapsum     Remap sum",
+    "                 Sum of the source points.",
+    "    remapmean    Remap mean",
+    "                 Mean of the source points.",
+    "    remapavg     Remap average",
+    "                 Average of the source points.",
+    "    remapstd     Remap standard deviation",
+    "                 Standard deviation of the source points. Normalize by n.",
+    "    remapstd1    Remap standard deviation (n-1)",
+    "                 Standard deviation of the source points. Normalize by (n-1).",
+    "    remapvar     Remap variance",
+    "                 Variance of the source points. Normalize by n.",
+    "    remapvar1    Remap variance (n-1)",
+    "                 Variance of the source points. Normalize by (n-1).",
+    "    remapskew    Remap skewness",
+    "                 Skewness of the source points.",
+    "    remapkurt    Remap kurtosis",
+    "                 Kurtosis of the source points.",
+    "    remapmedian  Remap median",
+    "                 Median of the source points.",
+    "",
+    "PARAMETER",
+    "    grid  STRING  Target grid description file or name",
+};
+
+const CdoHelp VertstatHelp = {
+    "NAME",
+    "    vertmin, vertmax, vertrange, vertsum, vertmean, vertavg, vertstd, vertstd1, ",
+    "    vertvar, vertvar1 - Vertical statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>,weights  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over all levels of the input variables.",
+    "    According to chosen operator the vertical minimum, maximum, range, sum, average, variance",
+    "    or standard deviation is written to outfile.",
+    "",
+    "OPERATORS",
+    "    vertmin    Vertical minimum",
+    "               For every gridpoint the minimum over all levels is computed.",
+    "    vertmax    Vertical maximum",
+    "               For every gridpoint the maximum over all levels is computed.",
+    "    vertrange  Vertical range",
+    "               For every gridpoint the range over all levels is computed.",
+    "    vertsum    Vertical sum",
+    "               For every gridpoint the sum over all levels is computed.",
+    "    vertmean   Vertical mean",
+    "               For every gridpoint the layer weighted mean over all levels is computed.",
+    "    vertavg    Vertical average",
+    "               For every gridpoint the layer weighted average over all levels is computed.",
+    "    vertstd    Vertical standard deviation",
+    "               For every gridpoint the standard deviation over all levels is computed. Normalize by n.",
+    "    vertstd1   Vertical standard deviation (n-1)",
+    "               For every gridpoint the standard deviation over all levels is computed. Normalize by (n-1).",
+    "    vertvar    Vertical variance",
+    "               For every gridpoint the variance over all levels is computed. Normalize by n.",
+    "    vertvar1   Vertical variance (n-1)",
+    "               For every gridpoint the variance over all levels is computed. Normalize by (n-1).",
+    "",
+    "PARAMETER",
+    "    weights  BOOL   weights=FALSE disables weighting by layer thickness [default: weights=TRUE]",
+};
+
+const CdoHelp TimselstatHelp = {
+    "NAME",
+    "    timselmin, timselmax, timselrange, timselsum, timselmean, timselavg, ",
+    "    timselstd, timselstd1, timselvar, timselvar1 - Time range statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>,nsets[,noffset[,nskip]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values for a selected number of timesteps. According to ",
+    "    the chosen operator the minimum, maximum, range, sum, average, variance or standard deviation of ",
+    "    the selected timesteps is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    timselmin    Time selection minimum",
+    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = min{i(t',x), t1 < t' <= tn}",
+    "    timselmax    Time selection maximum",
+    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = max{i(t',x), t1 < t' <= tn}",
+    "    timselrange  Time selection range",
+    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = range{i(t',x), t1 < t' <= tn}",
+    "    timselsum    Time selection sum",
+    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = sum{i(t',x), t1 < t' <= tn}",
+    "    timselmean   Time selection mean",
+    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = mean{i(t',x), t1 < t' <= tn}",
+    "    timselavg    Time selection average",
+    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = avg{i(t',x), t1 < t' <= tn}",
+    "    timselstd    Time selection standard deviation",
+    "                 Normalize by n. For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = std{i(t',x), t1 < t' <= tn}",
+    "    timselstd1   Time selection standard deviation (n-1)",
+    "                 Normalize by (n-1). For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = std1{i(t',x), t1 < t' <= tn}",
+    "    timselvar    Time selection variance",
+    "                 Normalize by n. For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = var{i(t',x), t1 < t' <= tn}",
+    "    timselvar1   Time selection variance (n-1)",
+    "                 Normalize by (n-1). For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "                 ",
+    "                 o(t,x) = var1{i(t',x), t1 < t' <= tn}",
+    "",
+    "PARAMETER",
+    "    nsets    INTEGER  Number of input timesteps for each output timestep ",
+    "    noffset  INTEGER  Number of input timesteps skipped before the first timestep range (optional)",
+    "    nskip    INTEGER  Number of input timesteps skipped between timestep ranges (optional)",
+};
+
+const CdoHelp TimselpctlHelp = {
+    "NAME",
+    "    timselpctl - Time range percentile values",
+    "",
+    "SYNOPSIS",
+    "    timselpctl,p,nsets[,noffset[,nskip]]  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentile values over a selected number of timesteps in infile1.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and infile3,",
+    "    respectively. The default number of histogram bins is 101. The default can be overridden by setting the",
+    "    environment variable CDO_PCTL_NBINS to a different value. The files infile2 and infile3 ",
+    "    should be the result of corresponding timselmin and timselmax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
+    "    ",
+    "    o(t,x) = pth percentile {i(t',x), t1 < t' <= tn}",
+    "",
+    "PARAMETER",
+    "    p        FLOAT    Percentile number in {0, ..., 100}",
+    "    nsets    INTEGER  Number of input timesteps for each output timestep ",
+    "    noffset  INTEGER  Number of input timesteps skipped before the first timestep range (optional)",
+    "    nskip    INTEGER  Number of input timesteps skipped between timestep ranges (optional)",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp RunstatHelp = {
+    "NAME",
+    "    runmin, runmax, runrange, runsum, runmean, runavg, runstd, runstd1, runvar, ",
+    "    runvar1 - Running statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>,nts  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes running statistical values over a selected number of timesteps. Depending on ",
+    "    the chosen operator the minimum, maximum, range, sum, average, variance or standard deviation of a selected ",
+    "    number of consecutive timesteps read from infile is written to outfile. ",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    runmin    Running minimum",
+    "              o(t+(nts-1)/2,x) = min{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runmax    Running maximum",
+    "              o(t+(nts-1)/2,x) = max{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runrange  Running range",
+    "              o(t+(nts-1)/2,x) = range{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runsum    Running sum",
+    "              o(t+(nts-1)/2,x) = sum{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runmean   Running mean",
+    "              o(t+(nts-1)/2,x) = mean{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runavg    Running average",
+    "              o(t+(nts-1)/2,x) = avg{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runstd    Running standard deviation",
+    "              Normalize by n. ",
+    "              ",
+    "              o(t+(nts-1)/2,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runstd1   Running standard deviation (n-1)",
+    "              Normalize by (n-1). ",
+    "              ",
+    "              o(t+(nts-1)/2,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runvar    Running variance",
+    "              Normalize by n. ",
+    "              ",
+    "              o(t+(nts-1)/2,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "    runvar1   Running variance (n-1)",
+    "              Normalize by (n-1). ",
+    "              ",
+    "              o(t+(nts-1)/2,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "",
+    "PARAMETER",
+    "    nts  INTEGER  Number of timesteps",
+    "",
+    "ENVIRONMENT",
+    "    CDO_TIMESTAT_DATE",
+    "        Sets the time stamp in outfile to the \"first\", \"middle\" or \"last\" contributing timestep of infile.",
+};
+
+const CdoHelp RunpctlHelp = {
+    "NAME",
+    "    runpctl - Running percentile values",
+    "",
+    "SYNOPSIS",
+    "    runpctl,p,nts  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes running percentiles over a selected number of timesteps in infile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    ",
+    "    o(t+(nts-1)/2,x) = pth percentile {i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
+    "",
+    "PARAMETER",
+    "    p    FLOAT    Percentile number in {0, ..., 100}",
+    "    nts  INTEGER  Number of timesteps",
+};
+
+const CdoHelp TimstatHelp = {
+    "NAME",
+    "    timmin, timmax, timrange, timsum, timmean, timavg, timstd, timstd1, timvar, ",
+    "    timvar1 - Statistical values over all timesteps",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over all timesteps in infile. Depending on ",
+    "    the chosen operator the minimum, maximum, range, sum, average, variance or standard deviation of ",
+    "    all timesteps read from infile is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    timmin    Time minimum",
+    "              o(1,x) = min{i(t',x), t_1<t'<=t_n}",
+    "    timmax    Time maximum",
+    "              o(1,x) = max{i(t',x), t_1<t'<=t_n}",
+    "    timrange  Time range",
+    "              o(1,x) = range{i(t',x), t_1<t'<=t_n}",
+    "    timsum    Time sum",
+    "              o(1,x) = sum{i(t',x), t_1<t'<=t_n}",
+    "    timmean   Time mean",
+    "              o(1,x) = mean{i(t',x), t_1<t'<=t_n}",
+    "    timavg    Time average",
+    "              o(1,x) = avg{i(t',x), t_1<t'<=t_n}",
+    "    timstd    Time standard deviation",
+    "              Normalize by n. ",
+    "              ",
+    "              o(1,x) = std{i(t',x), t_1<t'<=t_n}",
+    "    timstd1   Time standard deviation (n-1)",
+    "              Normalize by (n-1). ",
+    "              ",
+    "              o(1,x) = std1{i(t',x), t_1<t'<=t_n}",
+    "    timvar    Time variance",
+    "              Normalize by n. ",
+    "              ",
+    "              o(1,x) = var{i(t',x), t_1<t'<=t_n}",
+    "    timvar1   Time variance (n-1)",
+    "              Normalize by (n-1). ",
+    "              ",
+    "              o(1,x) = var1{i(t',x), t_1<t'<=t_n}",
+};
+
+const CdoHelp TimpctlHelp = {
+    "NAME",
+    "    timpctl - Percentile values over all timesteps",
+    "",
+    "SYNOPSIS",
+    "    timpctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentiles over all timesteps in infile1. The algorithm uses ",
+    "    histograms with minimum and maximum bounds given in infile2 and infile3, respectively. ",
+    "    The default number of histogram bins is 101. The default can be overridden by defining the",
+    "    environment variable CDO_PCTL_NBINS. The files infile2 and infile3 should be",
+    "    the result of corresponding timmin and timmax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    ",
+    "    o(1,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp HourstatHelp = {
+    "NAME",
+    "    hourmin, hourmax, hourrange, hoursum, hourmean, houravg, hourstd, hourstd1, ",
+    "    hourvar, hourvar1 - Hourly statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over timesteps of the same hour.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of timesteps of the same hour is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    hourmin    Hourly minimum",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = min{i(t',x), t_1<t'<=t_n}",
+    "    hourmax    Hourly maximum",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = max{i(t',x), t_1<t'<=t_n}",
+    "    hourrange  Hourly range",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = range{i(t',x), t_1<t'<=t_n}",
+    "    hoursum    Hourly sum",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
+    "    hourmean   Hourly mean",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
+    "    houravg    Hourly average",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
+    "    hourstd    Hourly standard deviation",
+    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = std{i(t',x), t_1<t'<=t_n}",
+    "    hourstd1   Hourly standard deviation (n-1)",
+    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = std1{i(t',x), t_1<t'<=t_n}",
+    "    hourvar    Hourly variance",
+    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = var{i(t',x), t_1<t'<=t_n}",
+    "    hourvar1   Hourly variance (n-1)",
+    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "               ",
+    "               o(t,x) = var1{i(t',x), t_1<t'<=t_n}",
+};
+
+const CdoHelp HourpctlHelp = {
+    "NAME",
+    "    hourpctl - Hourly percentile values",
+    "",
+    "SYNOPSIS",
+    "    hourpctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentiles over all timesteps of the same hour in infile1.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
+    "    infile3, respectively. The default number of histogram bins is 101.",
+    "    The default can be overridden by defining the environment variable CDO_PCTL_NBINS.",
+    "    The files infile2 and infile3 should be the result of corresponding hourmin",
+    "    and hourmax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
+    "    ",
+    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp DaystatHelp = {
+    "NAME",
+    "    daymin, daymax, dayrange, daysum, daymean, dayavg, daystd, daystd1, dayvar, ",
+    "    dayvar1 - Daily statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over timesteps of the same day.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of timesteps of the same day is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    daymin    Daily minimum",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = min{i(t',x), t_1<t'<=t_n}",
+    "    daymax    Daily maximum",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = max{i(t',x), t_1<t'<=t_n}",
+    "    dayrange  Daily range",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = range{i(t',x), t_1<t'<=t_n}",
+    "    daysum    Daily sum",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
+    "    daymean   Daily mean",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
+    "    dayavg    Daily average",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
+    "    daystd    Daily standard deviation",
+    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = std{i(t',x), t_1<t'<=t_n}",
+    "    daystd1   Daily standard deviation (n-1)",
+    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = std1{i(t',x), t_1<t'<=t_n}",
+    "    dayvar    Daily variance",
+    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = var{i(t',x), t_1<t'<=t_n}",
+    "    dayvar1   Daily variance (n-1)",
+    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "              ",
+    "              o(t,x) = var1{i(t',x), t_1<t'<=t_n}",
+};
+
+const CdoHelp DaypctlHelp = {
+    "NAME",
+    "    daypctl - Daily percentile values",
+    "",
+    "SYNOPSIS",
+    "    daypctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentiles over all timesteps of the same day in infile1.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
+    "    infile3, respectively. The default number of histogram bins is 101.",
+    "    The default can be overridden by defining the environment variable CDO_PCTL_NBINS.",
+    "    The files infile2 and infile3 should be the result of corresponding daymin",
+    "    and daymax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
+    "    ",
+    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp MonstatHelp = {
+    "NAME",
+    "    monmin, monmax, monrange, monsum, monmean, monavg, monstd, monstd1, monvar, ",
+    "    monvar1 - Monthly statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over timesteps of the same month.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of timesteps of the same month is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    monmin    Monthly minimum",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = min{i(t',x), t_1<t'<=t_n}",
+    "    monmax    Monthly maximum",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = max{i(t',x), t_1<t'<=t_n}",
+    "    monrange  Monthly range",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = range{i(t',x), t_1<t'<=t_n}",
+    "    monsum    Monthly sum",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
+    "    monmean   Monthly mean",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
+    "    monavg    Monthly average",
+    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
+    "    monstd    Monthly standard deviation",
+    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = std{i(t',x), t_1 < t' <= t_n}",
+    "    monstd1   Monthly standard deviation (n-1)",
+    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = std1{i(t',x), t_1 < t' <= t_n}",
+    "    monvar    Monthly variance",
+    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = var{i(t',x), t_1 < t' <= t_n}",
+    "    monvar1   Monthly variance (n-1)",
+    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "              ",
+    "              o(t,x) = var1{i(t',x), t_1 < t' <= t_n}",
+};
+
+const CdoHelp MonpctlHelp = {
+    "NAME",
+    "    monpctl - Monthly percentile values",
+    "",
+    "SYNOPSIS",
+    "    monpctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentiles over all timesteps of the same month in infile1.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
+    "    infile3, respectively. The default number of histogram bins is 101.",
+    "    The default can be overridden by defining the environment variable CDO_PCTL_NBINS.",
+    "    The files infile2 and infile3 should be the result of corresponding monmin",
+    "    and monmax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
+    "    ",
+    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp YearmonstatHelp = {
+    "NAME",
+    "    yearmonmean - Yearly mean from monthly data",
+    "",
+    "SYNOPSIS",
+    "    yearmonmean  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes the yearly mean of a monthly time series.",
+    "    Each month is weighted with the number of days per month. ",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    ",
+    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "    ",
+    "    o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_TIMESTAT_DATE",
+    "        Sets the date information in outfile to the \"first\", \"middle\" or \"last\" contributing timestep of infile.",
+};
+
+const CdoHelp YearstatHelp = {
+    "NAME",
+    "    yearmin, yearmax, yearminidx, yearmaxidx, yearrange, yearsum, yearmean, ",
+    "    yearavg, yearstd, yearstd1, yearvar, yearvar1 - Yearly statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over timesteps of the same year.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of timesteps of the same year is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    yearmin     Yearly minimum",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = min{i(t',x), t_1<t'<=t_n}",
+    "    yearmax     Yearly maximum",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = max{i(t',x), t_1<t'<=t_n}",
+    "    yearminidx  Yearly minimum indices",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = minidx{i(t',x), t_1<t'<=t_n}",
+    "    yearmaxidx  Yearly maximum indices",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = maxidx{i(t',x), t_1<t'<=t_n}",
+    "    yearrange   Yearly range",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = range{i(t',x), t_1<t'<=t_n}",
+    "    yearsum     Yearly sum",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
+    "    yearmean    Yearly mean",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
+    "    yearavg     Yearly average",
+    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
+    "    yearstd     Yearly standard deviation",
+    "                Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = std{i(t',x), t_1 < t' <= t_n}",
+    "    yearstd1    Yearly standard deviation (n-1)",
+    "                Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = std1{i(t',x), t_1 < t' <= t_n}",
+    "    yearvar     Yearly variance",
+    "                Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = var{i(t',x), t_1 < t' <= t_n}",
+    "    yearvar1    Yearly variance (n-1)",
+    "                Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "                ",
+    "                o(t,x) = var1{i(t',x), t_1 < t' <= t_n}",
+    "",
+    "NOTE",
+    "    The operators yearmean and yearavg compute only arithmetical means!",
+};
+
+const CdoHelp YearpctlHelp = {
+    "NAME",
+    "    yearpctl - Yearly percentile values",
+    "",
+    "SYNOPSIS",
+    "    yearpctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentiles over all timesteps of the same year in infile1.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
+    "    infile3, respectively. The default number of histogram bins is 101. The default can be",
+    "    overridden by defining the environment variable CDO_PCTL_NBINS. The files infile2 and",
+    "    infile3 should be the result of corresponding yearmin and yearmax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
+    "    ",
+    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp SeasstatHelp = {
+    "NAME",
+    "    seasmin, seasmax, seasrange, seassum, seasmean, seasavg, seasstd, seasstd1, ",
+    "    seasvar, seasvar1 - Seasonal statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values over timesteps of the same season.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of timesteps of the same season is written to outfile.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    Be careful about the first and the last output timestep, they may be incorrect values ",
+    "    if the seasons have incomplete timesteps.",
+    "",
+    "OPERATORS",
+    "    seasmin    Seasonal minimum",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = min{i(t',x), t1 < t' <= tn}",
+    "    seasmax    Seasonal maximum",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = max{i(t',x), t1 < t' <= tn}",
+    "    seasrange  Seasonal range",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = range{i(t',x), t1 < t' <= tn}",
+    "    seassum    Seasonal sum",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = sum{i(t',x), t1 < t' <= tn}",
+    "    seasmean   Seasonal mean",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = mean{i(t',x), t1 < t' <= tn}",
+    "    seasavg    Seasonal average",
+    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = avg{i(t',x), t1 < t' <= tn}",
+    "    seasstd    Seasonal standard deviation",
+    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = std{i(t',x), t1 < t' <= tn}",
+    "    seasstd1   Seasonal standard deviation (n-1)",
+    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = std1{i(t',x), t1 < t' <= tn}",
+    "    seasvar    Seasonal variance",
+    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = var{i(t',x), t1 < t' <= tn}",
+    "    seasvar1   Seasonal variance (n-1)",
+    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "               ",
+    "               o(t,x) = var1{i(t',x), t1 < t' <= tn}",
+};
+
+const CdoHelp SeaspctlHelp = {
+    "NAME",
+    "    seaspctl - Seasonal percentile values",
+    "",
+    "SYNOPSIS",
+    "    seaspctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes percentiles over all timesteps in infile1 of the same season.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and infile3,",
+    "    respectively. The default number of histogram bins is 101. The default can be overridden",
+    "    by defining the environment variable CDO_PCTL_NBINS. The files infile2 and infile3",
+    "    should be the result of corresponding seasmin and seasmax operations, respectively.",
+    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "    Be careful about the first and the last output timestep, they may be incorrect values ",
+    "    if the seasons have incomplete timesteps.",
+    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
+    "    ",
+    "    o(t,x) = pth percentile {i(t',x), t1 < t' <= tn}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp YhourstatHelp = {
+    "NAME",
+    "    yhourmin, yhourmax, yhourrange, yhoursum, yhourmean, yhouravg, yhourstd, ",
+    "    yhourstd1, yhourvar, yhourvar1 - Multi-year hourly statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of each hour and day of year.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of each hour and day of year in infile is written to outfile.",
+    "    The date information in an output field is the date of the last contributing input field.",
+    "",
+    "OPERATORS",
+    "    yhourmin    Multi-year hourly minimum",
+    "                o(0001,x) = min{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = min{i(t,x), day(i(t)) = 8784}",
+    "    yhourmax    Multi-year hourly maximum",
+    "                o(0001,x) = max{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = max{i(t,x), day(i(t)) = 8784}",
+    "    yhourrange  Multi-year hourly range",
+    "                o(0001,x) = range{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = range{i(t,x), day(i(t)) = 8784}",
+    "    yhoursum    Multi-year hourly sum",
+    "                o(0001,x) = sum{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = sum{i(t,x), day(i(t)) = 8784}",
+    "    yhourmean   Multi-year hourly mean",
+    "                o(0001,x) = mean{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = mean{i(t,x), day(i(t)) = 8784}",
+    "    yhouravg    Multi-year hourly average",
+    "                o(0001,x) = avg{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = avg{i(t,x), day(i(t)) = 8784}",
+    "    yhourstd    Multi-year hourly standard deviation",
+    "                Normalize by n. ",
+    "                ",
+    "                o(0001,x) = std{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = std{i(t,x), day(i(t)) = 8784}",
+    "    yhourstd1   Multi-year hourly standard deviation (n-1)",
+    "                Normalize by (n-1). ",
+    "                ",
+    "                o(0001,x) = std1{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = std1{i(t,x), day(i(t)) = 8784}",
+    "    yhourvar    Multi-year hourly variance",
+    "                Normalize by n. ",
+    "                ",
+    "                o(0001,x) = var{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = var{i(t,x), day(i(t)) = 8784}",
+    "    yhourvar1   Multi-year hourly variance (n-1)",
+    "                Normalize by (n-1). ",
+    "                ",
+    "                o(0001,x) = var1{i(t,x), day(i(t)) = 0001}",
+    "                                 ...",
+    "                o(8784,x) = var1{i(t,x), day(i(t)) = 8784}",
+};
+
+const CdoHelp DhourstatHelp = {
+    "NAME",
+    "    dhourmin, dhourmax, dhourrange, dhoursum, dhourmean, dhouravg, dhourstd, ",
+    "    dhourstd1, dhourvar, dhourvar1 - Multi-day hourly statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of each hour of day.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of each hour of day in infile is written to outfile.",
+    "    The date information in an output field is the date of the last contributing input field.",
+    "",
+    "OPERATORS",
+    "    dhourmin    Multi-day hourly minimum",
+    "                o(01,x) = min{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = min{i(t,x), day(i(t)) = 24}",
+    "    dhourmax    Multi-day hourly maximum",
+    "                o(01,x) = max{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = max{i(t,x), day(i(t)) = 24}",
+    "    dhourrange  Multi-day hourly range",
+    "                o(01,x) = range{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = range{i(t,x), day(i(t)) = 24}",
+    "    dhoursum    Multi-day hourly sum",
+    "                o(01,x) = sum{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = sum{i(t,x), day(i(t)) = 24}",
+    "    dhourmean   Multi-day hourly mean",
+    "                o(01,x) = mean{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = mean{i(t,x), day(i(t)) = 24}",
+    "    dhouravg    Multi-day hourly average",
+    "                o(01,x) = avg{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = avg{i(t,x), day(i(t)) = 24}",
+    "    dhourstd    Multi-day hourly standard deviation",
+    "                Normalize by n. ",
+    "                ",
+    "                o(01,x) = std{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = std{i(t,x), day(i(t)) = 24}",
+    "    dhourstd1   Multi-day hourly standard deviation (n-1)",
+    "                Normalize by (n-1). ",
+    "                ",
+    "                o(01,x) = std1{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = std1{i(t,x), day(i(t)) = 24}",
+    "    dhourvar    Multi-day hourly variance",
+    "                Normalize by n. ",
+    "                ",
+    "                o(01,x) = var{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = var{i(t,x), day(i(t)) = 24}",
+    "    dhourvar1   Multi-day hourly variance (n-1)",
+    "                Normalize by (n-1). ",
+    "                ",
+    "                o(01,x) = var1{i(t,x), day(i(t)) = 01}",
+    "                                 ...",
+    "                o(24,x) = var1{i(t,x), day(i(t)) = 24}",
+};
+
+const CdoHelp YdaystatHelp = {
+    "NAME",
+    "    ydaymin, ydaymax, ydayrange, ydaysum, ydaymean, ydayavg, ydaystd, ydaystd1, ",
+    "    ydayvar, ydayvar1 - Multi-year daily statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of each day of year.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of each day of year in infile is written to outfile.",
+    "    The date information in an output field is the date of the last contributing input field.",
+    "",
+    "OPERATORS",
+    "    ydaymin    Multi-year daily minimum",
+    "               o(001,x) = min{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = min{i(t,x), day(i(t)) = 366}",
+    "    ydaymax    Multi-year daily maximum",
+    "               o(001,x) = max{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = max{i(t,x), day(i(t)) = 366}",
+    "    ydayrange  Multi-year daily range",
+    "               o(001,x) = range{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = range{i(t,x), day(i(t)) = 366}",
+    "    ydaysum    Multi-year daily sum",
+    "               o(001,x) = sum{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = sum{i(t,x), day(i(t)) = 366}",
+    "    ydaymean   Multi-year daily mean",
+    "               o(001,x) = mean{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = mean{i(t,x), day(i(t)) = 366}",
+    "    ydayavg    Multi-year daily average",
+    "               o(001,x) = avg{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = avg{i(t,x), day(i(t)) = 366}",
+    "    ydaystd    Multi-year daily standard deviation",
+    "               Normalize by n. ",
+    "               ",
+    "               o(001,x) = std{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = std{i(t,x), day(i(t)) = 366}",
+    "    ydaystd1   Multi-year daily standard deviation (n-1)",
+    "               Normalize by (n-1). ",
+    "               ",
+    "               o(001,x) = std1{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = std1{i(t,x), day(i(t)) = 366}",
+    "    ydayvar    Multi-year daily variance",
+    "               Normalize by n. ",
+    "               ",
+    "               o(001,x) = var{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = var{i(t,x), day(i(t)) = 366}",
+    "    ydayvar1   Multi-year daily variance (n-1)",
+    "               Normalize by (n-1). ",
+    "               ",
+    "               o(001,x) = var1{i(t,x), day(i(t)) = 001}",
+    "                                ...",
+    "               o(366,x) = var1{i(t,x), day(i(t)) = 366}",
+};
+
+const CdoHelp YdaypctlHelp = {
+    "NAME",
+    "    ydaypctl - Multi-year daily percentile values",
+    "",
+    "SYNOPSIS",
+    "    ydaypctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator writes a certain percentile of each day of year in infile1 to outfile.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
+    "    infile3, respectively. The default number of histogram bins is 101. The default can be",
+    "    overridden by setting the environment variable CDO_PCTL_NBINS to a different value.",
+    "    The files infile2 and infile3 should be the result of corresponding ydaymin and",
+    "    ydaymax operations, respectively.",
+    "    The date information in an output field is the date of the last contributing input field.",
+    "    ",
+    "    o(001,x) = pth percentile {i(t,x), day(i(t)) = 001}",
+    "                     ...",
+    "    o(366,x) = pth percentile {i(t,x), day(i(t)) = 366}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp YmonstatHelp = {
+    "NAME",
+    "    ymonmin, ymonmax, ymonrange, ymonsum, ymonmean, ymonavg, ymonstd, ymonstd1, ",
+    "    ymonvar, ymonvar1 - Multi-year monthly statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of each month of year.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of each month of year in infile is written to outfile.",
+    "    The date information in an output field is the date of the last contributing input field.",
+    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
+    "",
+    "OPERATORS",
+    "    ymonmin    Multi-year monthly minimum",
+    "               o(01,x) = min{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = min{i(t,x), month(i(t)) = 12}",
+    "    ymonmax    Multi-year monthly maximum",
+    "               o(01,x) = max{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = max{i(t,x), month(i(t)) = 12}",
+    "    ymonrange  Multi-year monthly range",
+    "               o(01,x) = range{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = range{i(t,x), month(i(t)) = 12}",
+    "    ymonsum    Multi-year monthly sum",
+    "               o(01,x) = sum{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = sum{i(t,x), month(i(t)) = 12}",
+    "    ymonmean   Multi-year monthly mean",
+    "               o(01,x) = mean{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = mean{i(t,x), month(i(t)) = 12}",
+    "    ymonavg    Multi-year monthly average",
+    "               o(01,x) = avg{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = avg{i(t,x), month(i(t)) = 12}",
+    "    ymonstd    Multi-year monthly standard deviation",
+    "               Normalize by n. ",
+    "               ",
+    "               o(01,x) = std{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = std{i(t,x), month(i(t)) = 12}",
+    "    ymonstd1   Multi-year monthly standard deviation (n-1)",
+    "               Normalize by (n-1). ",
+    "               ",
+    "               o(01,x) = std1{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = std1{i(t,x), month(i(t)) = 12}",
+    "    ymonvar    Multi-year monthly variance",
+    "               Normalize by n. ",
+    "               ",
+    "               o(01,x) = var{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = var{i(t,x), month(i(t)) = 12}",
+    "    ymonvar1   Multi-year monthly variance (n-1)",
+    "               Normalize by (n-1). ",
+    "               ",
+    "               o(01,x) = var1{i(t,x), month(i(t)) = 01}",
+    "                                ...",
+    "               o(12,x) = var1{i(t,x), month(i(t)) = 12}",
+};
+
+const CdoHelp YmonpctlHelp = {
+    "NAME",
+    "    ymonpctl - Multi-year monthly percentile values",
+    "",
+    "SYNOPSIS",
+    "    ymonpctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator writes a certain percentile of each month of year in infile1 to outfile.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in",
+    "    infile2 and infile3, respectively. The default number of",
+    "    histogram bins is 101. The default can be overridden by setting the",
+    "    environment variable CDO_PCTL_NBINS to a different value. The files",
+    "    infile2 and infile3 should be the result of corresponding",
+    "    ymonmin and ymonmax operations, respectively.",
+    "    The date information in an output field is the date of the last",
+    "    contributing input field.",
+    "    ",
+    "    o(01,x) = pth percentile {i(t,x), month(i(t)) = 01}",
+    "                     ...",
+    "    o(12,x) = pth percentile {i(t,x), month(i(t)) = 12}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp YseasstatHelp = {
+    "NAME",
+    "    yseasmin, yseasmax, yseasrange, yseassum, yseasmean, yseasavg, yseasstd, ",
+    "    yseasstd1, yseasvar, yseasvar1 - Multi-year seasonal statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of each season.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of each season in infile is written to outfile.",
+    "    The date information in an output field is the date of the last contributing input field.",
+    "",
+    "OPERATORS",
+    "    yseasmin    Multi-year seasonal minimum",
+    "                o(1,x) = min{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = min{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = min{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = min{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasmax    Multi-year seasonal maximum",
+    "                o(1,x) = max{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = max{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = max{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = max{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasrange  Multi-year seasonal range",
+    "                o(1,x) = range{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = range{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = range{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = range{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseassum    Multi-year seasonal sum",
+    "                o(1,x) = sum{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = sum{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = sum{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = sum{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasmean   Multi-year seasonal mean",
+    "                o(1,x) = mean{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = mean{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = mean{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = mean{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasavg    Multi-year seasonal average",
+    "                o(1,x) = avg{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = avg{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = avg{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = avg{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasstd    Multi-year seasonal standard deviation",
+    "                o(1,x) = std{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = std{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = std{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = std{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasstd1   Multi-year seasonal standard deviation (n-1)",
+    "                o(1,x) = std1{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = std1{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = std1{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = std1{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasvar    Multi-year seasonal variance",
+    "                o(1,x) = var{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = var{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = var{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = var{i(t,x), month(i(t)) = 09, 10, 11}",
+    "    yseasvar1   Multi-year seasonal variance (n-1)",
+    "                o(1,x) = var1{i(t,x), month(i(t)) = 12, 01, 02}",
+    "                o(2,x) = var1{i(t,x), month(i(t)) = 03, 04, 05}",
+    "                o(3,x) = var1{i(t,x), month(i(t)) = 06, 07, 08}",
+    "                o(4,x) = var1{i(t,x), month(i(t)) = 09, 10, 11}",
+};
+
+const CdoHelp YseaspctlHelp = {
+    "NAME",
+    "    yseaspctl - Multi-year seasonal percentile values",
+    "",
+    "SYNOPSIS",
+    "    yseaspctl,p  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator writes a certain percentile of each season in infile1 to outfile.",
+    "    The algorithm uses histograms with minimum and maximum bounds given in",
+    "    infile2 and infile3, respectively. The default number of",
+    "    histogram bins is 101. The default can be overridden by setting the",
+    "    environment variable CDO_PCTL_NBINS to a different value. The files",
+    "    infile2 and infile3 should be the result of corresponding",
+    "    yseasmin and yseasmax operations, respectively.",
+    "    The date information in an output field is the date of the last",
+    "    contributing input field.",
+    "    ",
+    "    o(1,x) = pth percentile {i(t,x), month(i(t)) = 12, 01, 02}",
+    "    o(2,x) = pth percentile {i(t,x), month(i(t)) = 03, 04, 05}",
+    "    o(3,x) = pth percentile {i(t,x), month(i(t)) = 06, 07, 08}",
+    "    o(4,x) = pth percentile {i(t,x), month(i(t)) = 09, 10, 11}",
+    "",
+    "PARAMETER",
+    "    p  FLOAT  Percentile number in {0, ..., 100}",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp YdrunstatHelp = {
+    "NAME",
+    "    ydrunmin, ydrunmax, ydrunsum, ydrunmean, ydrunavg, ydrunstd, ydrunstd1, ",
+    "    ydrunvar, ydrunvar1 - Multi-year daily running statistical values",
+    "",
+    "SYNOPSIS",
+    "    <operator>,nts  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module writes running statistical values for each day of year in infile to outfile.",
+    "    Depending on the chosen operator, the minimum, maximum, sum, average, variance or standard deviation ",
+    "    of all timesteps in running windows of which the medium timestep corresponds to a certain day of",
+    "    year is computed. The date information in an output field is the date of the timestep in the middle ",
+    "    of the last contributing running window.",
+    "    Note that the operator have to be applied to a continuous time series of daily measurements in order ",
+    "    to yield physically meaningful results. Also note that the output time series begins (nts-1)/2 timesteps",
+    "    after the first timestep of the input time series and ends (nts-1)/2 timesteps before the last one.",
+    "    For input data which are complete but not continuous, such as time series of daily measurements for ",
+    "    the same month or season within different years, the operator yields physically meaningful results ",
+    "    only if the input time series does include the (nts-1)/2 days before and after each period of interest.",
+    "",
+    "OPERATORS",
+    "    ydrunmin   Multi-year daily running minimum",
+    "               o(001,x) = min{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = min{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "    ydrunmax   Multi-year daily running maximum",
+    "               o(001,x) = max{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = max{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "    ydrunsum   Multi-year daily running sum",
+    "               o(001,x) = sum{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = sum{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "    ydrunmean  Multi-year daily running mean",
+    "               o(001,x) = mean{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = mean{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "    ydrunavg   Multi-year daily running average",
+    "               o(001,x) = avg{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = avg{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "    ydrunstd   Multi-year daily running standard deviation",
+    "               Normalize by n. ",
+    "               ",
+    "               o(001,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 366}",
+    "    ydrunstd1  Multi-year daily running standard deviation (n-1)",
+    "               Normalize by (n-1). ",
+    "               ",
+    "               o(001,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 366}",
+    "    ydrunvar   Multi-year daily running variance",
+    "               Normalize by n. ",
+    "               ",
+    "               o(001,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "    ydrunvar1  Multi-year daily running variance (n-1)",
+    "               Normalize by (n-1). ",
+    "               ",
+    "               o(001,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                                ...",
+    "               o(366,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "",
+    "PARAMETER",
+    "    nts  INTEGER  Number of timesteps",
+};
+
+const CdoHelp YdrunpctlHelp = {
+    "NAME",
+    "    ydrunpctl - Multi-year daily running percentile values",
+    "",
+    "SYNOPSIS",
+    "    ydrunpctl,p,nts  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator writes running percentile values for each day of year in infile1 to outfile. ",
+    "    A certain percentile is computed for all timesteps in running windows of which the medium ",
+    "    timestep corresponds to a certain day of year. ",
+    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and infile3,",
+    "    respectively. The default number of histogram bins is 101. The default can be overridden",
+    "    by setting the environment variable CDO_PCTL_NBINS to a different value. The files infile2 ",
+    "    and infile3 should be the result of corresponding ydrunmin and ydrunmax operations, respectively.",
+    "    The date information in an output field is the date of the timestep in the middle of the last contributing running window.",
+    "    Note that the operator have to be applied to a continuous time series of daily measurements ",
+    "    in order to yield physically meaningful results. Also note that the output time series begins",
+    "    (nts-1)/2 timesteps after the first timestep of the input time series and ends (nts-1)/2 ",
+    "    timesteps before the last.",
+    "    For input data which are complete but not continuous, such as time series of daily measurements ",
+    "    for the same month or season within different years, the operator only yields physically meaningful ",
+    "    results if the input time series does include the (nts-1)/2 days before and after each period ",
+    "    of interest.",
+    "    ",
+    "    o(001,x) = pth percentile {i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
+    "                     ...",
+    "    o(366,x) = pth percentile {i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
+    "",
+    "PARAMETER",
+    "    p    FLOAT    Percentile number in {0, ..., 100}",
+    "    nts  INTEGER  Number of timesteps",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+const CdoHelp FldcorHelp = {
+    "NAME",
+    "    fldcor - Correlation in grid space",
+    "",
+    "SYNOPSIS",
+    "    fldcor  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    The correlation coefficient is a quantity that gives the quality of a least ",
+    "    squares fitting to the original data. This operator correlates all gridpoints",
+    "    of two fields for each timestep. With",
+    "    ",
+    "    S(t) = {x, i_1(t,x) != missval and i_2(t,x) != missval}",
+    "    ",
+    "    it is",
+    "    ",
+    "    o(t,1) = Cor{(i_1(t,x), i_2(t,x)), x_1 < x <= x_n}",
+    "    ",
+    "    where w(x) are the area weights obtained by the input streams.",
+    "    For every timestep t only those field elements x belong to the sample,",
+    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
+};
+
+const CdoHelp TimcorHelp = {
+    "NAME",
+    "    timcor - Correlation over time",
+    "",
+    "SYNOPSIS",
+    "    timcor  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    The correlation coefficient is a quantity that gives the quality of a least ",
+    "    squares fitting to the original data. This operator correlates each gridpoint",
+    "    of two fields over all timesteps. With",
+    "    ",
+    "    S(x) = {t, i_1(t,x) != missval and i_2(t,x) != missval}",
+    "    ",
+    "    it is",
+    "    ",
+    "    o(1,x) = Cor{(i_1(t,x), i_2(t,x)), t_1 < t <= t_n}",
+    "    ",
+    "    For every gridpoint x only those timesteps t belong to the sample,",
+    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
+};
+
+const CdoHelp FldcovarHelp = {
+    "NAME",
+    "    fldcovar - Covariance in grid space",
+    "",
+    "SYNOPSIS",
+    "    fldcovar  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator calculates the covariance of two fields over all gridpoints",
+    "    for each timestep. With",
+    "    ",
+    "    S(t) = {x, i_1(t,x) != missval and i_2(t,x) != missval}",
+    "    ",
+    "    it is",
+    "    ",
+    "    o(t,1) = Covar{(i_1(t,x), i_2(t,x)), x_1 < x <= x_n}",
+    "    ",
+    "    where w(x) are the area weights obtained by the input streams.",
+    "    For every timestep t only those field elements x belong to the sample,",
+    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
+};
+
+const CdoHelp TimcovarHelp = {
+    "NAME",
+    "    timcovar - Covariance over time",
+    "",
+    "SYNOPSIS",
+    "    timcovar  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator calculates the covariance of two fields at each gridpoint",
+    "    over all timesteps. With",
+    "    ",
+    "    S(x) = {t, i_1(t,x) != missval and i_2(t,x) != missval}",
+    "    ",
+    "    it is",
+    "    ",
+    "    o(1,x) = Covar{(i_1(t,x), i_2(t,x)), t_1 < t <= t_n}",
+    "    ",
+    "    For every gridpoint x only those timesteps t belong to the sample,",
+    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
+};
+
+const CdoHelp RegresHelp = {
+    "NAME",
+    "    regres - Regression",
+    "",
+    "SYNOPSIS",
+    "    regres[,equal]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    The values of the input file infile are assumed to be distributed as",
+    "    N(a+b*t,S^2) with unknown a, b and S^2. This operator estimates the",
+    "    parameter b. For every field element x only those timesteps ",
+    "    t belong to the sample S(x), which have i(t,x) NE miss.",
+    "    It is assumed that all timesteps are equidistant, if this is not the case set the parameter equal=false.",
+    "",
+    "PARAMETER",
+    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
+};
+
+const CdoHelp DetrendHelp = {
+    "NAME",
+    "    detrend - Detrend time series",
+    "",
+    "SYNOPSIS",
+    "    detrend[,equal]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Every time series in infile is linearly detrended. For every field element x ",
+    "    only those timesteps t belong to the sample S(x), which have i(t,x) NE miss.",
+    "    It is assumed that all timesteps are equidistant, if this is not the case set the parameter equal=false.",
+    "",
+    "PARAMETER",
+    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
+    "",
+    "NOTE",
+    "    This operator has to keep the fields of all timesteps concurrently in the memory.",
+    "    If not enough memory is available use the operators trend and subtrend.",
+};
+
+const CdoHelp TrendHelp = {
+    "NAME",
+    "    trend - Trend of time series",
+    "",
+    "SYNOPSIS",
+    "    trend[,equal]  infile outfile1 outfile2",
+    "",
+    "DESCRIPTION",
+    "    The values of the input file infile are assumed to be distributed as",
+    "    N(a+b*t,S^2) with unknown a, b and S^2. This operator estimates the",
+    "    parameter a and b. For every field element x only those timesteps ",
+    "    t belong to the sample S(x), which have i(t,x) NE miss.",
+    "    Thus the estimation for a is stored in outfile1 and that for b is stored ",
+    "    in outfile2. To subtract the trend from the data see operator subtrend.",
+    "    It is assumed that all timesteps are equidistant, if this is not the case set the parameter equal=false.",
+    "",
+    "PARAMETER",
+    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
+};
+
+const CdoHelp TrendarithHelp = {
+    "NAME",
+    "    addtrend, subtrend - Add or subtract a trend",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,equal]  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module is for adding or subtracting a trend computed by the operator trend.",
+    "",
+    "OPERATORS",
+    "    addtrend  Add trend",
+    "              It is",
+    "              ",
+    "              o(t,x) = i_1(t,x) + (i_2(1,x) + i_3(1,x)*t)",
+    "              where t is the timesteps.",
+    "    subtrend  Subtract trend",
+    "              It is",
+    "              ",
+    "              o(t,x) = i_1(t,x) - (i_2(1,x) + i_3(1,x)*t)",
+    "              where t is the timesteps.",
+    "",
+    "PARAMETER",
+    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
+};
+
+const CdoHelp EOFsHelp = {
+    "NAME",
+    "    eof, eoftime, eofspatial, eof3d - Empirical Orthogonal Functions",
+    "",
+    "SYNOPSIS",
+    "    <operator>,neof  infile outfile1 outfile2",
+    "",
+    "DESCRIPTION",
+    "    This module calculates empirical orthogonal functions of the data in infile ",
+    "    as the eigen values of the scatter matrix (covariance matrix) S of the data",
+    "    sample z(t). A more detailed description can be found above.",
+    "    ",
+    "    Please note, that the input data are assumed to be anomalies.",
+    "    ",
+    "    If operator eof is chosen, the EOFs are computed in either time or spatial",
+    "    space, whichever is the fastest. If the user already knows, which computation",
+    "    is faster, the module can be forced to perform a computation in time- or gridspace",
+    "    by using the operators eoftime or eofspatial, respectively. This can enhance ",
+    "    performance, especially for very long time series, where the number of timesteps",
+    "    is larger than the number of grid-points. Data in infile are assumed to be anomalies.",
+    "    If they are not, the behavior of this module is not well defined. ",
+    "    After execution outfile1 will contain all eigen-values and outfile2 the",
+    "    eigenvectors e_j. All EOFs and eigen-values are computed. However, only the first ",
+    "    neof EOFs are written to outfile2. Nonetheless, outfile1 contains all eigen-values. ",
+    "    ",
+    "    Missing values are not fully supported. Support is only checked for non-changing",
+    "    masks of missing values in time. Although there still will be results, they are",
+    "    not trustworthy, and a warning will occur. In the latter case we suggest to ",
+    "    replace missing values by 0 in infile. ",
+    "",
+    "OPERATORS",
+    "    eof         Calculate EOFs in spatial or time space",
+    "    eoftime     Calculate EOFs in time space",
+    "    eofspatial  Calculate EOFs in spatial space",
+    "    eof3d       Calculate 3-Dimensional EOFs in time space",
+    "",
+    "PARAMETER",
+    "    neof  INTEGER  Number of eigen functions",
+    "",
+    "ENVIRONMENT",
+    "    CDO_SVD_MODE   ",
+    "        Is used to choose the algorithm for eigenvalue calculation. Options are 'jacobi' for ",
+    "        a one-sided parallel jacobi-algorithm (only executed in parallel if -P flag is set)",
+    "        and  'danielson_lanczos' for a non-parallel d/l algorithm. The default setting is 'jacobi'.",
+    "    CDO_WEIGHT_MODE",
+    "        It is used to set the weight mode. The default is 'off'. Set it to 'on' for a weighted version.",
+    "    MAX_JACOBI_ITER",
+    "        Is the maximum integer number of annihilation sweeps that is executed if the ",
+    "        jacobi-algorithm is used to compute the eigen values. The default value is 12.",
+    "    FNORM_PRECISION",
+    "        Is the Frobenius norm of the matrix consisting of an annihilation pair",
+    "        of eigenvectors that is used to determine if the eigenvectors have reached ",
+    "        a sufficient level of convergence. If all annihilation-pairs of vectors have ",
+    "        a norm below this value, the computation is considered to have converged ",
+    "        properly. Otherwise, a warning will occur. The default value 1e-12.",
+};
+
+const CdoHelp EofcoeffHelp = {
+    "NAME",
+    "    eofcoeff - Principal coefficients of EOFs",
+    "",
+    "SYNOPSIS",
+    "    eofcoeff  infile1 infile2 obase",
+    "",
+    "DESCRIPTION",
+    "    This module calculates the time series of the principal coefficients for given EOF",
+    "    (empirical orthogonal functions) and data. Time steps in infile1 are assumed to be the EOFs,",
+    "    time steps in infile2 are assumed to be the time series.",
+    "    Note, that this operator calculates a non weighted dot product of the fields in infile1 and infile2.",
+    "    For consistency set the environment variable CDO_WEIGHT_MODE=off when using eof or eof3d.",
+    "    ",
+    "    There will be a separate file containing a time series of principal coefficients",
+    "    with time information from infile2 for each EOF in infile1. Output files will be",
+    "    numbered as <obase><neof><suffix> where neof+1 is the number of the EOF (timestep)",
+    "    in infile1 and suffix is the filename extension derived from the file format. ",
+    "",
+    "ENVIRONMENT",
+    "    CDO_FILE_SUFFIX",
+    "        Set the default file suffix. This suffix will be added to the output file ",
+    "        names instead of the filename extension derived from the file format. ",
+    "        Set this variable to NULL to disable the adding of a file suffix.",
+};
+
+const CdoHelp RemapbilHelp = {
+    "NAME",
+    "    remapbil, genbil - Bilinear interpolation",
+    "",
+    "SYNOPSIS",
+    "    remapbil,grid  infile outfile",
+    "    genbil,grid[,map3d]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators for a bilinear remapping of fields between grids in spherical coordinates.",
+    "    The interpolation is based on an adapted SCRIP library version. ",
+    "    For a detailed description of the interpolation method see SCRIP.",
+    "    This interpolation method only works on quadrilateral curvilinear source grids.",
+    "",
+    "OPERATORS",
+    "    remapbil  Bilinear interpolation",
+    "              Performs a bilinear interpolation on all input fields.",
+    "    genbil    Generate bilinear interpolation weights",
+    "              Generates bilinear interpolation weights for the first input field and writes the",
+    "              result to a file. The format of this file is NetCDF following the SCRIP convention.",
+    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
+    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
+    "",
+    "PARAMETER",
+    "    grid   STRING  Target grid description file or name",
+    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
+    "",
+    "ENVIRONMENT",
+    "    REMAP_EXTRAPOLATE",
+    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
+    "        By default the extrapolation is enabled for circular grids.",
+};
+
+const CdoHelp RemapbicHelp = {
+    "NAME",
+    "    remapbic, genbic - Bicubic interpolation",
+    "",
+    "SYNOPSIS",
+    "    remapbic,grid  infile outfile",
+    "    genbic,grid[,map3d]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators for a bicubic remapping of fields between grids in spherical coordinates.",
+    "    The interpolation is based on an adapted SCRIP library version. ",
+    "    For a detailed description of the interpolation method see SCRIP.",
+    "    This interpolation method only works on quadrilateral curvilinear source grids.",
+    "",
+    "OPERATORS",
+    "    remapbic  Bicubic interpolation",
+    "              Performs a bicubic interpolation on all input fields.",
+    "    genbic    Generate bicubic interpolation weights",
+    "              Generates bicubic interpolation weights for the first input field and writes the",
+    "              result to a file. The format of this file is NetCDF following the SCRIP convention.",
+    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
+    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
+    "",
+    "PARAMETER",
+    "    grid   STRING  Target grid description file or name",
+    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
+    "",
+    "ENVIRONMENT",
+    "    REMAP_EXTRAPOLATE",
+    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
+    "        By default the extrapolation is enabled for circular grids.",
+};
+
+const CdoHelp RemapnnHelp = {
+    "NAME",
+    "    remapnn, gennn - Nearest neighbor remapping",
+    "",
+    "SYNOPSIS",
+    "    remapnn,grid  infile outfile",
+    "    gennn,grid[,map3d]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators for a nearest neighbor remapping of fields between grids",
+    "    in spherical coordinates.",
+    "",
+    "OPERATORS",
+    "    remapnn  Nearest neighbor remapping",
+    "             Performs a nearest neighbor remapping on all input fields.",
+    "    gennn    Generate nearest neighbor remap weights",
+    "             Generates nearest neighbor remapping weights for the first input field and writes the result to a file.",
+    "             The format of this file is NetCDF following the SCRIP convention.",
+    "             Use the operator remap to apply this remapping weights to a data file with the same source grid.",
+    "             Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
+    "             In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
+    "",
+    "PARAMETER",
+    "    grid   STRING  Target grid description file or name",
+    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
+    "",
+    "ENVIRONMENT",
+    "    REMAP_EXTRAPOLATE    ",
+    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
+    "        By default the extrapolation is enabled for this remapping method.",
+    "    CDO_GRIDSEARCH_RADIUS",
+    "        Grid search radius in degree, default 180 degree.",
+};
+
+const CdoHelp RemapdisHelp = {
+    "NAME",
+    "    remapdis, gendis - Distance weighted average remapping",
+    "",
+    "SYNOPSIS",
+    "    remapdis,grid[,neighbors]  infile outfile",
+    "    gendis,grid[,neighbors[,map3d]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators for an inverse distance weighted average remapping of the four",
+    "    nearest neighbor values of fields between grids in spherical coordinates.",
+    "    The default number of 4 neighbors can be changed with the neighbors parameter.",
+    "",
+    "OPERATORS",
+    "    remapdis  Distance weighted average remapping",
+    "              Performs an inverse distance weighted averaged remapping of the nearest neighbor values on all input fields.",
+    "    gendis    Generate distance weighted average remap weights",
+    "              Generates distance weighted averaged remapping weights of the nearest neighbor values for the first input",
+    "              field and writes the result to a file. The format of this file is NetCDF following the SCRIP convention.",
+    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
+    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
+    "",
+    "PARAMETER",
+    "    grid       STRING   Target grid description file or name",
+    "    neighbors  INTEGER  Number of nearest neighbors [default: 4]",
+    "    map3d      BOOL    Generate all mapfiles of the first 3D field",
+    "",
+    "ENVIRONMENT",
+    "    REMAP_EXTRAPOLATE    ",
+    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
+    "        By default the extrapolation is enabled for this remapping method.",
+    "    CDO_GRIDSEARCH_RADIUS",
+    "        Grid search radius in degree, default 180 degree.",
+};
+
+const CdoHelp RemapconHelp = {
+    "NAME",
+    "    remapcon, gencon - First order conservative remapping",
+    "",
+    "SYNOPSIS",
+    "    remapcon,grid  infile outfile",
+    "    gencon,grid[,map3d]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators for a first order conservative remapping of fields between grids in spherical coordinates.",
+    "    The operators in this module uses code from the YAC software package to compute the conservative remapping weights.",
+    "    For a detailed description of the interpolation method see YAC.",
+    "    The interpolation method is completely general and can be used for any grid on a sphere.",
+    "    The search algorithm for the conservative remapping requires that no grid cell occurs more than once. ",
+    "",
+    "OPERATORS",
+    "    remapcon  First order conservative remapping",
+    "              Performs a first order conservative remapping on all input fields.",
+    "    gencon    Generate 1st order conservative remap weights",
+    "              Generates first order conservative remapping weights for the first input field and",
+    "              writes the result to a file. The format of this file is NetCDF following the SCRIP convention.",
+    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
+    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
+    "",
+    "PARAMETER",
+    "    grid   STRING  Target grid description file or name",
+    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
+    "",
+    "ENVIRONMENT",
+    "    CDO_REMAP_NORM",
+    "        This variable is used to choose the normalization of the conservative interpolation. ",
+    "        By default CDO_REMAP_NORM is set to 'fracarea'. 'fracarea' uses the sum of the",
+    "        non-masked source cell intersected areas to normalize each target cell field value.",
+    "        This results in a reasonable flux value but the flux is not locally conserved.",
+    "        The option 'destarea' uses the total target cell area to normalize each target cell",
+    "        field value. Local flux conservation is ensured, but unreasonable flux values may result.",
+    "    REMAP_AREA_MIN",
+    "        This variable is used to set the minimum destination area fraction. The default",
+    "        of this variable is 0.0.",
+};
+
+const CdoHelp RemaplafHelp = {
+    "NAME",
+    "    remaplaf, genlaf - Largest area fraction remapping",
+    "",
+    "SYNOPSIS",
+    "    <operator>,grid  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators for a largest area fraction remapping of fields between grids in spherical coordinates.",
+    "    The operators in this module uses code from the YAC software package to compute the largest area fraction.",
+    "    For a detailed description of the interpolation method see YAC.",
+    "    The interpolation method is completely general and can be used for any grid on a sphere.",
+    "    The search algorithm for this remapping method requires that no grid cell occurs more than once. ",
+    "",
+    "OPERATORS",
+    "    remaplaf  Largest area fraction remapping",
+    "              Performs a largest area fraction remapping on all input fields.",
+    "    genlaf    Generate largest area fraction remap weights",
+    "              Generates largest area fraction remapping weights for the first input field and",
+    "              writes the result to a file. The format of this file is NetCDF following the SCRIP convention.",
+    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
+    "",
+    "PARAMETER",
+    "    grid  STRING  Target grid description file or name",
+    "",
+    "ENVIRONMENT",
+    "    REMAP_AREA_MIN",
+    "        This variable is used to set the minimum destination area fraction. The default",
+    "        of this variable is 0.0.",
+};
+
+const CdoHelp RemapHelp = {
+    "NAME",
+    "    remap - Grid remapping",
+    "",
+    "SYNOPSIS",
+    "    remap,grid,weights  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Interpolation between different horizontal grids can be a very time-consuming ",
+    "    process. Especially if the data are on an unstructured and/or a large grid. ",
+    "    In this case the interpolation process can be split into two parts.",
+    "    Firstly the generation of the interpolation weights, which is the most time-consuming part.",
+    "    These interpolation weights can be reused for every remapping process with the operator remap.",
+    "    This operator remaps all input fields to a new horizontal grid. The remap type and ",
+    "    the interpolation weights of one input grid are read from a NetCDF file. More weights ",
+    "    are computed if the input fields are on different grids. The NetCDF file with the ",
+    "    weights should follow the SCRIP convention. Normally these weights come from a previous",
+    "    call to one of the genXXX operators (e.g. genbil) or were created by the original SCRIP package.",
+    "",
+    "PARAMETER",
+    "    grid     STRING  Target grid description file or name",
+    "    weights  STRING  Interpolation weights (SCRIP NetCDF file)",
+    "",
+    "ENVIRONMENT",
+    "    CDO_REMAP_NORM       ",
+    "        This variable is used to choose the normalization of the conservative interpolation. ",
+    "        By default CDO_REMAP_NORM is set to 'fracarea'. 'fracarea' uses the sum of the",
+    "        non-masked source cell intersected areas to normalize each target cell field value.",
+    "        This results in a reasonable flux value but the flux is not locally conserved.",
+    "        The option 'destarea' uses the total target cell area to normalize each target cell",
+    "        field value. Local flux conservation is ensured, but unreasonable flux values may result.",
+    "    REMAP_EXTRAPOLATE    ",
+    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
+    "        By default the extrapolation is enabled for remapdis, remapnn and for circular grids.",
+    "    REMAP_AREA_MIN       ",
+    "        This variable is used to set the minimum destination area fraction. The default",
+    "        of this variable is 0.0.",
+    "    CDO_GRIDSEARCH_RADIUS",
+    "        Grid search radius in degree, default 180 degree.",
+};
+
+const CdoHelp RemapetaHelp = {
+    "NAME",
+    "    remapeta - Remap vertical hybrid level",
+    "",
+    "SYNOPSIS",
+    "    remapeta,vct[,oro]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator interpolates between different vertical hybrid levels. This include the preparation ",
+    "    of consistent data for the free atmosphere. The procedure for the vertical interpolation is based ",
+    "    on the HIRLAM scheme and was adapted from INTERA.",
+    "    The vertical interpolation is based on the vertical integration of the hydrostatic equation with ",
+    "    few adjustments. The basic tasks are the following one:",
+    "    - at first integration of hydrostatic equation",
+    "    - extrapolation of surface pressure",
+    "    - Planetary Boundary-Layer (PBL) proutfile interpolation",
+    "    - interpolation in free atmosphere",
+    "    - merging of both proutfiles",
+    "    - final surface pressure correction",
+    "    ",
+    "    The vertical interpolation corrects the surface pressure. This is simply a cut-off or an addition ",
+    "    of air mass. This mass correction should not influence the geostrophic velocity field in the middle ",
+    "    troposhere. Therefore the total mass above a given reference level is conserved. As reference level",
+    "    the geopotential height of the 400 hPa level is used. Near the surface the correction can affect ",
+    "    the vertical structure of the PBL. Therefore the interpolation is done using the potential temperature. ",
+    "    But in the free atmosphere above a certain n (n=0.8 defining the top of the PBL) the interpolation ",
+    "    is done linearly. After the interpolation both proutfiles are merged. With the resulting ",
+    "    temperature/pressure correction the hydrostatic equation is integrated again and adjusted to the ",
+    "    reference level finding the final surface pressure correction. A more detailed description of",
+    "    the interpolation can be found in INTERA. This operator requires all variables on the same horizontal grid.",
+    "",
+    "PARAMETER",
+    "    vct  STRING  File name of an ASCII dataset with the vertical coordinate table",
+    "    oro  STRING  File name with the orography (surf. geopotential) of the target dataset (optional)",
+    "",
+    "ENVIRONMENT",
+    "    REMAPETA_PTOP",
+    "        Sets the minimum pressure level for condensation.",
+    "        Above this level the humidity is set to the constant 1.E-6.",
+    "        The default value is 0 Pa.",
+    "",
+    "NOTE",
+    "    The code numbers or the variable names of the required parameter have to follow the ECHAM convention.",
+    "    ",
+    "    Use the sinfo command to test if your vertical coordinate system is recognized as hybrid system.",
+    "    ",
+    "    In case remapeta complains about not finding any data on hybrid model levels you may wish",
+    "    to use the setzaxis command to generate a zaxis description which conforms to the ECHAM convention.",
+    "    See section \"1.4 Z-axis description\" for an example how to define a hybrid Z-axis.",
+};
+
+const CdoHelp VertintmlHelp = {
+    "NAME",
+    "    ml2pl, ml2hl - Vertical interpolation",
+    "",
+    "SYNOPSIS",
+    "    ml2pl,plevels  infile outfile",
+    "    ml2hl,hlevels  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Interpolates 3D variables on hybrid sigma pressure level to pressure or height levels.",
+    "    To calculate the pressure on model levels, the a and b coefficients defining the model levels and",
+    "    the surface pressure are required. The a and b coefficients are normally part of the model level data.",
+    "    If not available, the surface pressure can be derived from the logarithm of the surface pressure.",
+    "    To extrapolate the temperature, the surface geopotential is also needed.",
+    "    The geopotential height must be present at the hybrid layer interfaces (model half-layers)!",
+    "    All needed variables are identified by their GRIB1 code number or NetCDF CF standard name.",
+    "    Supported parameter tables are: WMO standard table number 2 and ECMWF local table number 128.",
+    "    ",
+    "     Name                       & Units      & GRIB1 code & CF standard name",
+    "       log surface pressure     &  Pa        &  152       &",
+    "       surface pressure         &  Pa        &  134       &  surface_air_pressure",
+    "       air temperature          &  K         &  130       &  air_temperature",
+    "       surface geopotential     &  m2 s-2    &  129       &  surface_geopotential",
+    "       geopotential height      &  m         &  156       &  geopotential_height",
+    "    ",
+    "    Use the alias  ml2plx/ml2hlx or the environment variable EXTRAPOLATE to extrapolate",
+    "    missing values. This operator requires all variables on the same horizontal grid.",
+    "    Missing values in the input data are not supported.",
+    "    ",
+    "",
+    "OPERATORS",
+    "    ml2pl  Model to pressure level interpolation",
+    "           Interpolates 3D variables on hybrid sigma pressure level to pressure level.",
+    "    ml2hl  Model to height level interpolation",
+    "           Interpolates 3D variables on hybrid sigma pressure level to height level.",
+    "           The procedure is the same as for the operator ml2pl except for",
+    "           the pressure levels being calculated from the heights by:",
+    "           plevel = 101325*exp(hlevel/-7000)",
+    "",
+    "PARAMETER",
+    "    plevels  FLOAT  Pressure levels in pascal",
+    "    hlevels  FLOAT  Height levels in meter",
+    "",
+    "ENVIRONMENT",
+    "    EXTRAPOLATE",
+    "        If set to 1 extrapolate missing values.",
+    "",
+    "NOTE",
+    "    The components of the hybrid coordinate must always be avaiable at the hybrid layer interfaces even if the data is defined at the hybrid layer midpoints.",
+};
+
+const CdoHelp VertintapHelp = {
+    "NAME",
+    "    ap2pl - Vertical pressure interpolation",
+    "",
+    "SYNOPSIS",
+    "    ap2pl,plevels  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Interpolate 3D variables on hybrid sigma height coordinates to pressure levels.",
+    "    The input file must contain the 3D air pressure in pascal. The air pressure is",
+    "    identified by the NetCDF CF standard name air_pressure.",
+    "    Use the alias  ap2plx or the environment variable EXTRAPOLATE to extrapolate",
+    "    missing values. This operator requires all variables on the same horizontal grid.",
+    "",
+    "PARAMETER",
+    "    plevels  FLOAT  Comma-separated list of pressure levels in pascal",
+    "",
+    "ENVIRONMENT",
+    "    EXTRAPOLATE",
+    "        If set to 1 extrapolate missing values.",
+    "",
+    "NOTE",
+    "    This is a specific implementation for NetCDF files from the ICON model, it may not work with data from other sources.",
+};
+
+const CdoHelp VertintghHelp = {
+    "NAME",
+    "    gh2hl - Vertical height interpolation",
+    "",
+    "SYNOPSIS",
+    "    gh2hl,hlevels  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Interpolate 3D variables on hybrid sigma height coordinates to height levels.",
+    "    The input file must contain the 3D geometric height in meter. The geometric height is",
+    "    identified by the NetCDF CF standard name geometric_height_at_full_level_center.",
+    "    Use the alias  gh2hlx or the environment variable EXTRAPOLATE to extrapolate",
+    "    missing values. This operator requires all variables on the same horizontal grid.",
+    "",
+    "PARAMETER",
+    "    hlevels  FLOAT  Comma-separated list of height levels in meter",
+    "",
+    "ENVIRONMENT",
+    "    EXTRAPOLATE",
+    "        If set to 1 extrapolate missing values.",
+    "",
+    "NOTE",
+    "    This is a specific implementation for NetCDF files from the ICON model, it may not work with data from other sources.",
+};
+
+const CdoHelp IntlevelHelp = {
+    "NAME",
+    "    intlevel - Linear level interpolation",
+    "",
+    "SYNOPSIS",
+    "    intlevel,parameter  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator performs a linear vertical interpolation of 3D variables. The 1D target levels can be",
+    "    specified with the level parameter or read in via a Z-axis description file.",
+    "",
+    "PARAMETER",
+    "    level             FLOAT   Comma-separated list of target levels",
+    "    zaxisdescription  STRING  Path to a file containing a description of the Z-axis",
+    "    zvarname          STRING  Use zvarname as the vertical 3D source coordinate instead of the 1D coordinate variable",
+};
+
+const CdoHelp Intlevel3dHelp = {
+    "NAME",
+    "    intlevel3d, intlevelx3d - ",
+    "    Linear level interpolation from/to 3D vertical coordinates",
+    "",
+    "SYNOPSIS",
+    "    <operator>,tgtcoordinate  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator performs a linear vertical interpolation of 3D variables fields with given 3D vertical coordinates.",
+    "    infile1 contains the 3D data variables and infile2 the 3D vertical source coordinate. The parameter tgtcoordinate",
+    "    is a datafile with the 3D vertical target coordinate.",
+    "",
+    "OPERATORS",
+    "    intlevel3d   Linear level interpolation onto a 3D vertical coordinate",
+    "    intlevelx3d  like intlevel3d but with extrapolation",
+    "",
+    "PARAMETER",
+    "    tgtcoordinate  STRING  filename for 3D vertical target coordinates",
+};
+
+const CdoHelp InttimeHelp = {
+    "NAME",
+    "    inttime, intntime - Time interpolation",
+    "",
+    "SYNOPSIS",
+    "    inttime,date,time[,inc]  infile outfile",
+    "    intntime,n  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module performs linear interpolation between timesteps.",
+    "    Interpolation is only performed if both values exist.",
+    "    If both values are missing values, the result is also a missing value.",
+    "    If only one value exists, it is taken if the time weighting is greater than or equal to 0.5.",
+    "    So no new value will be created at existing time steps, if the value is missing there.",
+    "",
+    "OPERATORS",
+    "    inttime   Interpolation between timesteps",
+    "              This operator creates a new dataset by linear interpolation between timesteps.",
+    "              The user has to define the start date/time with an optional increment.",
+    "    intntime  Interpolation between timesteps",
+    "              This operator performs linear interpolation between timesteps.",
+    "              The user has to define the number of timesteps from one timestep to the next.",
+    "",
+    "PARAMETER",
+    "    date  STRING  Start date (format YYYY-MM-DD)",
+    "    time  STRING  Start time (format hh:mm:ss)",
+    "    inc   STRING  Optional increment (seconds, minutes, hours, days, months, years) [default: 0hour]",
+    "    n     INTEGER Number of timesteps from one timestep to the next",
+};
+
+const CdoHelp IntyearHelp = {
+    "NAME",
+    "    intyear - Year interpolation",
+    "",
+    "SYNOPSIS",
+    "    intyear,years  infile1 infile2 obase",
+    "",
+    "DESCRIPTION",
+    "    This operator performs linear interpolation between two years, timestep by timestep.",
+    "    The input files need to have the same structure with the same variables.",
+    "    The output files will be named <obase><yyyy><suffix> where yyyy will be the year and ",
+    "    suffix is the filename extension derived from the file format.",
+    "",
+    "PARAMETER",
+    "    years  INTEGER  Comma-separated list or first/last[/inc] range of years",
+    "",
+    "ENVIRONMENT",
+    "    CDO_FILE_SUFFIX",
+    "        Set the default file suffix. This suffix will be added to the output file ",
+    "        names instead of the filename extension derived from the file format. ",
+    "        Set this variable to NULL to disable the adding of a file suffix.",
+    "",
+    "NOTE",
+    "    This operator needs to open all output files simultaneously.",
+    "    The maximum number of open files depends on the operating system!",
+};
+
+const CdoHelp SpectralHelp = {
+    "NAME",
+    "    sp2gp, gp2sp - Spectral transformation",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,type|trunc]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module transforms fields on a global regular Gaussian grid to spectral coefficients and vice versa.",
+    "    The transformation is achieved by applying Fast Fourier Transformation (FFT) first and direct Legendre",
+    "    Transformation afterwards in gp2sp. In sp2gp the inverse Legendre Transformation and inverse FFT are used.",
+    "    Missing values are not supported.",
+    "    ",
+    "    The relationship between the spectral resolution, governed by the truncation number T, and the grid",
+    "    resolution depends on the number of grid points at which the shortest wavelength field is represented.",
+    "    For a grid with 2N points between the poles (so 4N grid points in total around the globe) the relationship is:",
+    "    ",
+    "             linear grid: the shortest wavelength is represented by 2 grid points → 4N ≃ 2(TL + 1)",
+    "    ",
+    "          quadratic grid: the shortest wavelength is represented by 3 grid points → 4N ≃ 3(TQ + 1)",
+    "    ",
+    "              cubic grid: the shortest wavelength is represented by 4 grid points → 4N ≃ 4(TC + 1)",
+    "    ",
+    "    The quadratic grid is used by ECHAM and ERA15. ERA40 is using a linear Gaussian grid reflected by the TL notation.",
+    "    ",
+    "    The following table shows the calculation of the number of latitudes and the triangular truncation for the different grid types:",
+    "    ",
+    "         Gridtype           & Number of latitudes: nlat   & Triangular truncation: ntr  ",
+    "           linear           &     NINT((ntr*2 + 1)/2)     &     (nlat*2 - 1) / 2",
+    "           quadratic        &     NINT((ntr*3 + 1)/2)     &     (nlat*2 - 1) / 3",
+    "           cubic            &     NINT((ntr*4 + 1)/2)     &     (nlat*2 - 1) / 4",
+    "",
+    "OPERATORS",
+    "    sp2gp  Spectral to gridpoint",
+    "           Convert all spectral fields to a global regular Gaussian grid.",
+    "           The optional parameter trunc must be greater than the input truncation.",
+    "    gp2sp  Gridpoint to spectral",
+    "           Convert all Gaussian gridpoint fields to spectral fields.",
+    "           The optional parameter trunc must be lower than the input truncation.",
+    "",
+    "PARAMETER",
+    "    type   STRING  Type of the grid: quadratic, linear, cubic (default: type=quadratic)",
+    "    trunc  STRING  Triangular truncation",
+    "",
+    "NOTE",
+    "    To speed up the calculations, the Legendre polynoms are kept in memory. This requires a relatively large",
+    "    amount of memory. This is for example 12GB for T1279 data.",
+};
+
+const CdoHelp SpecconvHelp = {
+    "NAME",
+    "    sp2sp - Spectral conversion",
+    "",
+    "SYNOPSIS",
+    "    sp2sp,trunc  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Changed the triangular truncation of all spectral fields. This operator performs downward ",
+    "    conversion by cutting the resolution. Upward conversions are achieved by filling in zeros.",
+    "",
+    "PARAMETER",
+    "    trunc  INTEGER  New spectral resolution",
+};
+
+const CdoHelp Wind2Help = {
+    "NAME",
+    "    dv2ps - D and V to velocity potential and stream function",
+    "",
+    "SYNOPSIS",
+    "    dv2ps  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Calculate spherical harmonic coefficients of velocity potential and stream function from ",
+    "    spherical harmonic coefficients of relative divergence and vorticity. The divergence and ",
+    "    vorticity need to have the names sd and svo or code numbers 155 and 138.",
+};
+
+const CdoHelp WindHelp = {
+    "NAME",
+    "    dv2uv, uv2dv - Wind transformation",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,gridtype]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module converts relative divergence and vorticity to U and V wind and vice versa.",
+    "    Divergence and vorticity are spherical harmonic coefficients in spectral space and",
+    "    U and V are on a global regular Gaussian grid. The Gaussian latitudes need to be ordered from",
+    "    north to south. Missing values are not supported.",
+    "    ",
+    "    The relationship between the spectral resolution, governed by the truncation number T, and the grid",
+    "    resolution depends on the number of grid points at which the shortest wavelength field is represented.",
+    "    For a grid with 2N points between the poles (so 4N grid points in total around the globe) the relationship is:",
+    "    ",
+    "             linear grid: the shortest wavelength is represented by 2 grid points → 4N ≃ 2(TL + 1)",
+    "    ",
+    "          quadratic grid: the shortest wavelength is represented by 3 grid points → 4N ≃ 3(TQ + 1)",
+    "    ",
+    "              cubic grid: the shortest wavelength is represented by 4 grid points → 4N ≃ 4(TC + 1)",
+    "    ",
+    "    The quadratic grid is used by ECHAM and ERA15. ERA40 is using a linear Gaussian grid reflected by the TL notation.",
+    "    ",
+    "    The following table shows the calculation of the number of latitudes and the triangular truncation for the different grid types:",
+    "    ",
+    "         Gridtype           & Number of latitudes: nlat   & Triangular truncation: ntr  ",
+    "           linear           &     NINT((ntr*2 + 1)/2)     &     (nlat*2 - 1) / 2",
+    "           quadratic        &     NINT((ntr*3 + 1)/2)     &     (nlat*2 - 1) / 3",
+    "           cubic            &     NINT((ntr*4 + 1)/2)     &     (nlat*2 - 1) / 4",
+    "",
+    "OPERATORS",
+    "    dv2uv  Divergence and vorticity to U and V wind",
+    "           Calculate U and V wind on a Gaussian grid from spherical harmonic ",
+    "           coefficients of relative divergence and vorticity. The divergence and vorticity ",
+    "           need to have the names sd and svo or code numbers 155 and 138.",
+    "    uv2dv  U and V wind to divergence and vorticity",
+    "           Calculate spherical harmonic coefficients of relative divergence and vorticity",
+    "           from U and V wind. The U and V wind need to be on a Gaussian grid and need to have the ",
+    "           names u and v or the code numbers 131 and 132.",
+    "",
+    "PARAMETER",
+    "    gridtype  STRING  Type of the grid: quadratic, linear, cubic (default: quadratic)",
+    "",
+    "NOTE",
+    "    To speed up the calculations, the Legendre polynoms are kept in memory. This requires a relatively large",
+    "    amount of memory. This is for example 12GB for T1279 data.",
+};
+
+const CdoHelp FourierHelp = {
+    "NAME",
+    "    fourier - Fourier transformation",
+    "",
+    "SYNOPSIS",
+    "    fourier,epsilon  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    The fourier operator performs the fourier transformation or the inverse fourier transformation of all input fields.",
+    "    If the number of timesteps is a power of 2 then the algorithm of the Fast Fourier Transformation (FFT) is used.",
+    "    ",
+    "    ",
+    "    If the input stream infile consists only of complex fields, then the fields of outfile, computed by",
+    "       cdo -f ext fourier,1 -fourier,-1 infile outfile",
+    "    are the same than that of infile. For real input files see function retocomplex.",
+    "",
+    "PARAMETER",
+    "    epsilon  INTEGER  -1: forward transformation;  1: backward transformation",
+    "",
+    "NOTE",
+    "    Complex numbers can only be stored in NetCDF4 and EXTRA format.",
+};
+
+const CdoHelp ImportbinaryHelp = {
+    "NAME",
+    "    import_binary - Import binary data sets",
+    "",
+    "SYNOPSIS",
+    "    import_binary  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator imports gridded binary data sets via a GrADS data descriptor file.",
+    "    The GrADS data descriptor file contains a complete description of the binary data as well ",
+    "    as instructions on where to find the data and how to read it. The descriptor file is an ASCII ",
+    "    file that can be created easily with a text editor. The general contents of a gridded data ",
+    "    descriptor file are as follows:",
+    "    - Filename for the binary data",
+    "    - Missing or undefined data value",
+    "    - Mapping between grid coordinates and world coordinates",
+    "    - Description of variables in the binary data set ",
+    "    ",
+    "    A detailed description of the components of a GrADS data descriptor file can be found in GrADS.",
+    "    Here is a list of the supported components:",
+    "    BYTESWAPPED, CHSUB, DSET, ENDVARS, FILEHEADER, HEADERBYTES, OPTIONS, TDEF, TITLE, ",
+    "    TRAILERBYTES, UNDEF, VARS, XDEF, XYHEADER, YDEF, ZDEF",
+    "",
+    "NOTE",
+    "    Only 32-bit IEEE floats are supported for standard binary files!",
+};
+
+const CdoHelp ImportcmsafHelp = {
+    "NAME",
+    "    import_cmsaf - Import CM-SAF HDF5 files",
+    "",
+    "SYNOPSIS",
+    "    import_cmsaf  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator imports gridded CM-SAF (Satellite Application Facility on Climate Monitoring)",
+    "    HDF5 files. CM-SAF exploits data from polar-orbiting and geostationary satellites in order ",
+    "    to provide climate monitoring products of the following parameters: ",
+    "    ",
+    "    Cloud parameters: cloud fraction (CFC), cloud type (CTY), cloud phase (CPH), ",
+    "                      cloud top height, pressure and temperature (CTH,CTP,CTT), ",
+    "                      cloud optical thickness (COT), cloud water path (CWP).",
+    "    ",
+    "    Surface radiation components: Surface albedo (SAL); surface incoming (SIS) ",
+    "                      and net (SNS) shortwave radiation; surface downward (SDL) ",
+    "                      and outgoing (SOL) longwave radiation, surface net longwave ",
+    "                      radiation (SNL) and surface radiation budget (SRB).",
+    "    ",
+    "    Top-of-atmosphere radiation components: Incoming (TIS) and reflected (TRS) ",
+    "                      solar radiative flux at top-of-atmosphere. Emitted thermal ",
+    "                      radiative flux at top-of-atmosphere (TET).",
+    "    ",
+    "    Water vapour:     Vertically integrated water vapour (HTW), layered vertically ",
+    "                      integrated water vapour and layer mean temperature and relative ",
+    "                      humidity for 5 layers (HLW), temperature and mixing ratio at ",
+    "                      6 pressure levels. ",
+    "    ",
+    "    Daily and monthly mean products can be ordered via the CM-SAF web page (www.cmsaf.eu). ",
+    "    Products with higher spatial and temporal resolution, i.e. instantaneous swath-based products,",
+    "    are available on request (contact.cmsaf@dwd.de). All products are distributed free-of-charge.",
+    "    More information on the data is available on the CM-SAF homepage (www.cmsaf.eu).",
+    "    ",
+    "    Daily and monthly mean products are provided in equal-area projections. CDO reads the ",
+    "    projection parameters from the metadata in the HDF5-headers in order to allow spatial ",
+    "    operations like remapping. For spatial operations with instantaneous products on original ",
+    "    satellite projection, additional files with arrays of latitudes and longitudes are needed.",
+    "    These can be obtained from CM-SAF together with the data.",
+    "    ",
+    "",
+    "NOTE",
+    "    To use this operator, it is necessary to build CDO with HDF5 support (version 1.6 or higher).",
+    "    The PROJ library (version 5.0 or higher) is needed for full support of the remapping",
+    "    functionality. ",
+};
+
+const CdoHelp ImportamsrHelp = {
+    "NAME",
+    "    import_amsr - Import AMSR binary files",
+    "",
+    "SYNOPSIS",
+    "    import_amsr  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator imports gridded binary AMSR (Advanced Microwave Scanning Radiometer) data.",
+    "    The binary data files are available from the AMSR ftp site (ftp://ftp.ssmi.com/amsre).",
+    "    Each file consists of twelve (daily) or five (averaged) 0.25 x 0.25 degree ",
+    "    grid (1440,720) byte maps. For daily files, six daytime maps in the following",
+    "    order, Time (UTC), Sea Surface Temperature (SST), 10 meter Surface Wind Speed (WSPD),",
+    "    Atmospheric Water Vapor (VAPOR), Cloud Liquid Water (CLOUD), and Rain Rate (RAIN), ",
+    "    are followed by six nighttime maps in the same order. Time-Averaged files contain ",
+    "    just the geophysical layers in the same order [SST, WSPD, VAPOR, CLOUD, RAIN].",
+    "    More information to the data is available on the AMSR homepage http://www.remss.com/amsr.",
+};
+
+const CdoHelp InputHelp = {
+    "NAME",
+    "    input, inputsrv, inputext - Formatted input",
+    "",
+    "SYNOPSIS",
+    "    input,grid[,zaxis]  outfile",
+    "    inputsrv  outfile",
+    "    inputext  outfile",
+    "",
+    "DESCRIPTION",
+    "    This module reads time series of one 2D variable from standard input.",
+    "    All input fields need to have the same horizontal grid. The format of the ",
+    "    input depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    input     ASCII input",
+    "              Reads fields with ASCII numbers from standard input and stores them",
+    "              in outfile. The numbers read are exactly that ones which are written ",
+    "              out by the output operator.",
+    "    inputsrv  SERVICE ASCII input",
+    "              Reads fields with ASCII numbers from standard input and stores them ",
+    "              in outfile. Each field should have a header of 8 integers (SERVICE likely).",
+    "              The numbers that are read are exactly that ones which are written out by ",
+    "              the outputsrv operator.",
+    "    inputext  EXTRA ASCII input",
+    "              Read fields with ASCII numbers from standard input and stores them ",
+    "              in outfile. Each field should have header of 4 integers (EXTRA likely).",
+    "              The numbers read are exactly that ones which are written out by ",
+    "              the outputext operator.",
+    "",
+    "PARAMETER",
+    "    grid   STRING  Grid description file or name",
+    "    zaxis  STRING  Z-axis description file",
+};
+
+const CdoHelp OutputHelp = {
+    "NAME",
+    "    output, outputf, outputint, outputsrv, outputext - Formatted output",
+    "",
+    "SYNOPSIS",
+    "    output  infiles",
+    "    outputf,format[,nelem]  infiles",
+    "    outputint  infiles",
+    "    outputsrv  infiles",
+    "    outputext  infiles",
+    "",
+    "DESCRIPTION",
+    "    This module prints all values of all input datasets to standard output.",
+    "    All input fields need to have the same horizontal grid. All input files ",
+    "    need to have the same structure with the same variables.",
+    "    The format of the output depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    output     ASCII output",
+    "               Prints all values to standard output.",
+    "               Each row has 6 elements with the C-style format \"%13.6g\".",
+    "    outputf    Formatted output",
+    "               Prints all values to standard output.",
+    "               The format and number of elements for each row have to be specified by the parameters",
+    "               format and nelem. The default for nelem is 1.",
+    "    outputint  Integer output",
+    "               Prints all values rounded to the nearest integer to standard output.",
+    "    outputsrv  SERVICE ASCII output",
+    "               Prints all values to standard output.",
+    "               Each field with a header of 8 integers (SERVICE likely).",
+    "    outputext  EXTRA ASCII output",
+    "               Prints all values to standard output.",
+    "               Each field with a header of 4 integers (EXTRA likely).",
+    "",
+    "PARAMETER",
+    "    format  STRING  C-style format for one element (e.g. %13.6g)",
+    "    nelem   INTEGER Number of elements for each row (default: nelem = 1)",
+};
+
+const CdoHelp OutputtabHelp = {
+    "NAME",
+    "    outputtab - Table output",
+    "",
+    "SYNOPSIS",
+    "    outputtab,parameter  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator prints a table of all input datasets to standard output.",
+    "    infiles is an arbitrary number of input files. All input files need to have ",
+    "    the same structure with the same variables on different timesteps.",
+    "    All input fields need to have the same horizontal grid.",
+    "    ",
+    "    The contents of the table depends on the chosen parameters. The format of each table parameter is keyname[:len].",
+    "    len is the optional length of a table entry. The number of significant digits of floating point parameters",
+    "    can be set with the CDO option --precision, the default is 7.",
+    "    Here is a list of all valid keynames:",
+    "    ",
+    "     Keyname    & Type    & Description      ",
+    "     value      & FLOAT   & Value of the variable [len:8]",
+    "     name       & STRING  & Name of the variable [len:8]",
+    "     param      & STRING  & Parameter ID (GRIB1: code[.tabnum]; GRIB2: num[.cat[.dis]]) [len:11]",
+    "     code       & INTEGER & Code number [len:4]",
+    "     x          & FLOAT   & X coordinate of the original grid [len:6]",
+    "     y          & FLOAT   & Y coordinate of the original grid [len:6]",
+    "     lon        & FLOAT   & Longitude coordinate in degrees [len:6]",
+    "     lat        & FLOAT   & Latitude coordinate in degrees [len:6]",
+    "     lev        & FLOAT   & Vertical level [len:6]",
+    "     xind       & INTEGER & Grid x index [len:4]",
+    "     yind       & INTEGER & Grid y index [len:4]",
+    "     timestep   & INTEGER & Timestep number [len:6]",
+    "     date       & STRING  & Date (format YYYY-MM-DD) [len:10]",
+    "     time       & STRING  & Time (format hh:mm:ss) [len:8]",
+    "     year       & INTEGER & Year [len:5]",
+    "     month      & INTEGER & Month [len:2]",
+    "     day        & INTEGER & Day [len:2]",
+    "     nohead     & INTEGER & Disable output of header line",
+    "",
+    "PARAMETER",
+    "    parameter  STRING   Comma-separated list of keynames, one for each column of the table",
+};
+
+const CdoHelp OutputgmtHelp = {
+    "NAME",
+    "    gmtxyz, gmtcells - GMT output",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile",
+    "",
+    "DESCRIPTION",
+    "    This module prints the first field of the input dataset to standard output.",
+    "    The output can be used to generate 2D Lon/Lat plots with GMT.",
+    "    The format of the output depends on the chosen operator.",
+    "",
+    "OPERATORS",
+    "    gmtxyz    GMT xyz format",
+    "              The operator exports the first field to the GMT xyz ASCII format.",
+    "              The output can be used to create contour plots with the GMT module pscontour.",
+    "    gmtcells  GMT multiple segment format",
+    "              The operator exports the first field to the GMT multiple segment ASCII format.",
+    "              The output can be used to create shaded gridfill plots with the GMT module psxy.",
+};
+
+const CdoHelp GradsdesHelp = {
+    "NAME",
+    "    gradsdes - GrADS data descriptor file",
+    "",
+    "SYNOPSIS",
+    "    gradsdes[,mapversion]  infile",
+    "",
+    "DESCRIPTION",
+    "    Creates a GrADS data descriptor file. Supported file formats are GRIB1, NetCDF, SERVICE, ",
+    "    EXTRA and IEG. For GRIB1 files the GrADS map file is also generated. For SERVICE and EXTRA",
+    "    files the grid have to be specified with the CDO option '-g <grid>'. This module takes infile",
+    "    in order to create filenames for the descriptor (infile.ctl) and the map (infile.gmp) file.",
+    "",
+    "PARAMETER",
+    "    mapversion  INTEGER  Format version of the GrADS map file for GRIB1 datasets. Use 1 for a machine",
+    "                specific version 1 GrADS map file, 2 for a machine independent version 2 GrADS map file",
+    "                and 4 to support GRIB files >2GB. ",
+    "                A version 2 map file can be used only with GrADS version 1.8 or newer.",
+    "                A version 4 map file can be used only with GrADS version 2.0 or newer.",
+    "                The default is 4 for files >2GB, otherwise 2.",
+};
+
+const CdoHelp AfterburnerHelp = {
+    "NAME",
+    "    after - ECHAM standard post processor",
+    "",
+    "SYNOPSIS",
+    "    after[,vct]  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    The \"afterburner\" is the standard post processor for ECHAM GRIB and NetCDF data which provides the following operations:",
+    "    ",
+    "    - Extract specified variables and levels",
+    "    - Compute derived variables",
+    "    - Transform spectral data to Gaussian grid representation",
+    "    - Vertical interpolation to pressure levels",
+    "    - Compute temporal means",
+    "    ",
+    "    This operator reads selection parameters as namelist from stdin.",
+    "    Use the UNIX redirection \"<namelistfile\" to read the namelist from file.",
+    "    ",
+    "    The input files can't be combined with other CDO operators because of an optimized reader for this operator.",
+    "",
+    "NAMELIST",
+    "    Namelist parameter and there defaults:",
+    "      TYPE=0, CODE=-1, LEVEL=-1, INTERVAL=0, MEAN=0, EXTRAPOLATE=1",
+    "    ",
+    "    TYPE controls the transformation and vertical interpolation. Transforming spectral data to Gaussian grid",
+    "    representation and vertical interpolation to pressure levels are performed in a chain of steps.",
+    "    The TYPE parameter may be used to stop the chain at a certain step. Valid values are:",
+    "    ",
+    "      TYPE  =  0 : Hybrid   level spectral coefficients",
+    "      TYPE  = 10 : Hybrid   level fourier  coefficients",
+    "      TYPE  = 11 : Hybrid   level zonal mean sections",
+    "      TYPE  = 20 : Hybrid   level gauss grids",
+    "      TYPE  = 30 : Pressure level gauss grids",
+    "      TYPE  = 40 : Pressure level fourier  coefficients",
+    "      TYPE  = 41 : Pressure level zonal mean sections",
+    "      TYPE  = 50 : Pressure level spectral coefficients",
+    "      TYPE  = 60 : Pressure level fourier  coefficients",
+    "      TYPE  = 61 : Pressure level zonal mean sections",
+    "      TYPE  = 70 : Pressure level gauss grids",
+    "    ",
+    "    Vorticity, divergence, streamfunction and velocity potential need special treatment in the vertical transformation.",
+    "    They are not available as types 30, 40 and 41. If you select one of these combinations, type is automatically",
+    "    switched to the equivalent types 70, 60 and 61. The type of all other variables will be switched too, because ",
+    "    the type is a global parameter.",
+    "    ",
+    "    CODE selects the variables by the ECHAM GRIB1 code number (1-255). The default value -1 processes all detected codes.",
+    "    Derived variables computed by the afterburner:",
+    "    ",
+    "    Code  & Name      & Longname                       & Units & Level       & Needed Codes",
+    "     34   & low_cld   & low cloud                      &       & single      & 223 on modellevel  ",
+    "     35   & mid_cld   & mid cloud                      &       & single      & 223 on modellevel  ",
+    "     36   & hih_cld   & high cloud                     &       & single      & 223 on modellevel  ",
+    "     131  & u         & u-velocity                     & m/s   & atm (ml+pl) & 138, 155           ",
+    "     132  & v         & v-velocity                     & m/s   & atm (ml+pl) & 138, 155           ",
+    "     135  & omega     & vertical velocity              & Pa/s  & atm (ml+pl) & 138, 152, 155      ",
+    "     148  & stream    & streamfunction                 & m^2/s & atm (ml+pl) & 131, 132           ",
+    "     149  & velopot   & velocity potential             & m^2/s & atm (ml+pl) & 131, 132           ",
+    "     151  & slp       & mean sea level pressure        & Pa    & surface     & 129, 130, 152       ",
+    "     156  & geopoth   & geopotential height            & m     & atm (ml+pl) & 129, 130, 133, 152 ",
+    "     157  & rhumidity & relative humidity              &       & atm (ml+pl) & 130, 133, 152      ",
+    "     189  & sclfs     & surface solar cloud forcing    &       & surface     & 176-185            ",
+    "     190  & tclfs     & surface thermal cloud forcing  &       & surface     & 177-186            ",
+    "     191  & sclf0     & top solar cloud forcing        &       & surface     & 178-187             ",
+    "     192  & tclf0     & top thermal cloud forcing      &       & surface     & 179-188            ",
+    "     259  & windspeed & windspeed                      & m/s   & atm (ml+pl) & sqrt(u*u+v*v)      ",
+    "     260  & precip    & total precipitation            &       & surface     & 142+143            ",
+    "    ",
+    "    LEVEL selects the hybrid or pressure levels. The allowed values depends on the parameter TYPE.",
+    "    The default value -1 processes all detected levels.",
+    "    ",
+    "    INTERVAL selects the processing interval. The default value 0 process data on monthly intervals.",
+    "    INTERVAL=1 sets the interval to daily.",
+    "    ",
+    "    MEAN=1 compute and write monthly or daily mean fields. The default value 0 writes out all timesteps.",
+    "    ",
+    "    EXTRAPOLATE=0 switch of the extrapolation of missing values during the interpolation from model to pressure",
+    "    level (only available with MEAN=0 and TYPE=30). The default value 1 extrapolate missing values.",
+    "    ",
+    "    Possible combinations of TYPE, CODE and MEAN:",
+    "    ",
+    "          TYPE   & CODE                    & MEAN",
+    "        0/10/11  & 130  temperature        &  0",
+    "        0/10/11  & 131  u-velocity         &  0",
+    "        0/10/11  & 132  v-velocity         &  0",
+    "        0/10/11  & 133  specific humidity  &  0",
+    "        0/10/11  & 138  vorticity          &  0",
+    "        0/10/11  & 148  streamfunction     &  0",
+    "        0/10/11  & 149  velocity potential &  0",
+    "        0/10/11  & 152  LnPs               &  0",
+    "        0/10/11  & 155  divergence         &  0",
+    "         >11     & all codes               &  0/1",
+    "",
+    "PARAMETER",
+    "    vct  STRING  File with VCT in ASCII format",
+};
+
+const CdoHelp FilterHelp = {
+    "NAME",
+    "    bandpass, lowpass, highpass - Time series filtering",
+    "",
+    "SYNOPSIS",
+    "    bandpass,fmin,fmax  infile outfile",
+    "    lowpass,fmax  infile outfile",
+    "    highpass,fmin  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module takes the time series for each gridpoint in infile and (fast fourier) transforms it ",
+    "    into the frequency domain. According to the particular operator and its parameters certain frequencies ",
+    "    are filtered (set to zero) in the frequency domain and the spectrum is (inverse fast fourier) transformed ",
+    "    back into the time domain.",
+    "    To determine the frequency the time-axis of infile is used. (Data should have a constant time increment ",
+    "    since this assumption applies for transformation. However, the time increment has to be different from zero.)",
+    "    All frequencies given as parameter are interpreted per year. This is done by the assumption of a 365-day calendar. ",
+    "    Consequently if you want to perform multiyear-filtering accurately you have to delete the 29th of February. ",
+    "    If your infile has a 360 year calendar the frequency parameters fmin respectively fmax should be ",
+    "    multiplied with a factor of 360/365 in order to obtain accurate results.  ",
+    "    For the set up of a frequency filter the frequency parameters have to be adjusted to a frequency in the data. ",
+    "    Here fmin is rounded down and fmax is always rounded up. Consequently it is possible to use bandpass with ",
+    "    fmin=fmax without getting a zero-field for outfile. ",
+    "    Hints for efficient usage: ",
+    "    - to get reliable results the time-series has to be detrended (cdo detrend)",
+    "    - the lowest frequency greater zero that can be contained in infile is 1/(N*dT), ",
+    "    - the greatest frequency is 1/(2dT) (Nyquist frequency),",
+    "    with N the number of timesteps and dT the time increment of infile in years.",
+    "    ",
+    "    Missing value support for operators in this module is not implemented, yet!",
+    "",
+    "OPERATORS",
+    "    bandpass  Bandpass filtering",
+    "              Bandpass filtering (pass for frequencies between fmin and fmax).",
+    "              Suppresses all variability outside the frequency range specified by [fmin,fmax].",
+    "    lowpass   Lowpass filtering",
+    "              Lowpass filtering (pass for frequencies lower than fmax).",
+    "              Suppresses all variability with frequencies greater than fmax. ",
+    "    highpass  Highpass filtering",
+    "              Highpass filtering (pass for frequencies greater than fmin). ",
+    "              Suppresses all variabilty with frequencies lower than fmin. ",
+    "",
+    "PARAMETER",
+    "    fmin  FLOAT	Minimum frequency per year that passes the filter.",
+    "    fmax  FLOAT	Maximum frequency per year that passes the filter.  ",
+    "",
+    "NOTE",
+    "    For better performace of these operators use the CDO configure option --with-fftw3.",
+};
+
+const CdoHelp GridcellHelp = {
+    "NAME",
+    "    gridarea, gridweights - Grid cell quantities",
+    "",
+    "SYNOPSIS",
+    "    gridarea[,radius]  infile outfile",
+    "    gridweights  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module reads the grid cell area of the first grid from the input stream. If the grid cell area is missing it",
+    "    will be computed from the grid coordinates. The area of a grid cell is calculated using spherical triangles from",
+    "    the coordinates of the center and the vertices. The base is a unit sphere which is scaled with the radius of the planet.",
+    "    The default planet radius is 6371000 meter. The parameter radius or the environment variable PLANET_RADIUS can be used to change the default.",
+    "    Depending on the chosen operator the grid cell area or weights are written to the output stream.",
+    "",
+    "OPERATORS",
+    "    gridarea     Grid cell area",
+    "                 Writes the grid cell area to the output stream. If the grid cell area have to",
+    "                 be computed it is scaled with the planet radius to square meters.",
+    "    gridweights  Grid cell weights",
+    "                 Writes the grid cell area weights to the output stream.",
+    "",
+    "PARAMETER",
+    "    radius  FLOAT   Planet radius in meter",
+    "",
+    "ENVIRONMENT",
+    "    PLANET_RADIUS",
+    "        This variable is used to scale the computed grid cell areas to square meters. ",
+    "        By default PLANET_RADIUS is set to an earth radius of 6371000 meter.",
+};
+
+const CdoHelp SmoothHelp = {
+    "NAME",
+    "    smooth, smooth9 - Smooth grid points",
+    "",
+    "SYNOPSIS",
+    "    smooth[,options]  infile outfile",
+    "    smooth9  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Smooth all grid points of a horizontal grid.",
+    "    Options is a comma-separated list of \"key=value\" pairs with optional parameters.",
+    "",
+    "OPERATORS",
+    "    smooth   Smooth grid points",
+    "             Performs a N point smoothing on all input fields. The number of points used depend",
+    "             on the search radius (radius) and the maximum number of points (maxpoints).",
+    "             Per default all points within the search radius of 1degree are used.",
+    "             The weights for the points depend on the form of the curve and the distance.",
+    "             The implemented form of the curve is linear with constant default weights of 0.25",
+    "             at distance 0 (weight0) and at the search radius (weightR).",
+    "    smooth9  9 point smoothing",
+    "             Performs a 9 point smoothing on all fields with a quadrilateral curvilinear grid.",
+    "             The result at each grid point is a weighted average of the grid point plus",
+    "             the 8 surrounding points. The center point receives a weight of 1.0, the ",
+    "             points at each side and above and below receive a weight of 0.5, and corner ",
+    "             points receive a weight of 0.3.",
+    "             All 9 points are multiplied by their weights and summed, then divided by ",
+    "             the total weight to obtain the smoothed value. Any missing data points are ",
+    "             not included in the sum; points beyond the grid boundary are considered to ",
+    "             be missing. Thus the final result may be the result of an averaging with less ",
+    "             than 9 points.",
+    "",
+    "PARAMETER",
+    "    nsmooth    INTEGER  Number of times to smooth, default nsmooth=1",
+    "    radius     STRING   Search radius, default radius=1deg (units: deg, rad, km, m)",
+    "    maxpoints  INTEGER  Maximum number of points, default maxpoints=<gridsize>",
+    "    form       STRING   Form of the curve, default form=linear",
+    "    weight0    FLOAT    Weight at distance 0, default weight0=0.25",
+    "    weightR    FLOAT    Weight at the search radius, default weightR=0.25",
+};
+
+const CdoHelp DeltatHelp = {
+    "NAME",
+    "    deltat - Difference between timesteps",
+    "",
+    "SYNOPSIS",
+    "    deltat  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator computes the difference between each timestep.",
+};
+
+const CdoHelp ReplacevaluesHelp = {
+    "NAME",
+    "    setvals, setrtoc, setrtoc2 - Replace variable values",
+    "",
+    "SYNOPSIS",
+    "    setvals,oldval,newval[,...]  infile outfile",
+    "    setrtoc,rmin,rmax,c  infile outfile",
+    "    setrtoc2,rmin,rmax,c,c2  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module replaces old variable values with new values, depending on the operator.",
+    "",
+    "OPERATORS",
+    "    setvals   Set list of old values to new values",
+    "              Supply a list of n pairs of old and new values.",
+    "    setrtoc   Set range to constant",
+    "                       / c      if i(t,x) GE rmin AND i(t,x) LE rmax",
+    "              o(t,x) = ",
+    "                       \\ i(t,x) if i(t,x) LT rmin AND i(t,x) GT rmax",
+    "    setrtoc2  Set range to constant others to constant2",
+    "                       / c      if i(t,x) GE rmin AND i(t,x) LE rmax",
+    "              o(t,x) = ",
+    "                       \\ c2     if i(t,x) LT rmin AND i(t,x) GT rmax",
+    "",
+    "PARAMETER",
+    "    oldval,newval,...  FLOAT   Pairs of old and new values",
+    "    rmin               FLOAT   Lower bound",
+    "    rmax               FLOAT   Upper bound",
+    "    c                  FLOAT   New value - inside range",
+    "    c2                 FLOAT   New value - outside range",
+};
+
+const CdoHelp GetgridcellHelp = {
+    "NAME",
+    "    gridcellindex - Get grid cell index",
+    "",
+    "SYNOPSIS",
+    "    gridcellindex[,parameter]  infile",
+    "",
+    "DESCRIPTION",
+    "    Get the grid cell index of one grid point selected by the parameter lon and lat.",
+    "",
+    "PARAMETER",
+    "    lon  INTEGER   Longitude of the grid cell in degree",
+    "    lat  INTEGER   Latitude of the grid cell in degree",
+};
+
+const CdoHelp VargenHelp = {
+    "NAME",
+    "    const, random, topo, seq, stdatm - Generate a field",
+    "",
+    "SYNOPSIS",
+    "    const,const,grid  outfile",
+    "    random,grid[,seed]  outfile",
+    "    topo[,grid]  outfile",
+    "    seq,start,end[,inc]  outfile",
+    "    stdatm,levels  outfile",
+    "",
+    "DESCRIPTION",
+    "    Generates a dataset with one or more fields",
+    "",
+    "OPERATORS",
+    "    const   Create a constant field",
+    "            Creates a constant field. All field elements of the grid have the same value.",
+    "    random  Create a field with random numbers",
+    "            Creates a field with rectangularly distrubuted random numbers in the interval [0,1].",
+    "    topo    Create a field with topography",
+    "            Creates a field with topography data, per default on a global half degree grid.",
+    "    seq     Create a time series",
+    "            Creates a time series with field size 1 and field elements beginning with a start value in time step 1",
+    "            which is increased from one time step to the next.",
+    "    stdatm  Create values for pressure and temperature for hydrostatic atmosphere",
+    "            Creates pressure and temperature values for the given list of vertical levels.",
+    "            The formulars are:",
+    "            ",
+    "            P(z) = P_0 * exp(-1 * g/R * H/T_0 * log( (exp(z/H)*T_0 + T_Delta)/(T_0 + T_Delta))",
+    "            T(z) = T_0 + T_Delta * exp(-z/H)",
+    "            ",
+    "            with the following constants",
+    "            ",
+    "            T_0     = 213 K           Offset to get a surface temperature of 288K",
+    "            T_Delta = 75 K            Temperature lapse rate for 10Km",
+    "            P_0     = 1013.25 hPa     Surface pressure",
+    "            H       = 10000.0 m       Scale height",
+    "            g       = 9.80665 m/s**2  Earth gravity",
+    "            R       = 287.05 J/kg*K   Gas constant for air",
+    "            ",
+    "            This is the solution for the hydrostatic equations and is only valid for the",
+    "            troposphere (constant positive lapse rate). The temperature increase in the",
+    "            stratosphere and other effects of the upper atmosphere are not taken into",
+    "            account.",
+    "",
+    "PARAMETER",
+    "    const   FLOAT   Constant",
+    "    seed    INTEGER The seed for a new sequence of pseudo-random numbers [default: 1]",
+    "    grid    STRING  Target grid description file or name",
+    "    start   FLOAT   Start value of the loop",
+    "    end     FLOAT   End value of the loop",
+    "    inc     FLOAT   Increment of the loop [default: 1]",
+    "    levels  FLOAT   Target levels in metre above surface",
+};
+
+const CdoHelp TimsortHelp = {
+    "NAME",
+    "    timsort - Timsort",
+    "",
+    "SYNOPSIS",
+    "    timsort  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Sorts the elements in ascending order over all timesteps for every field position.",
+    "    After sorting it is:",
+    "    ",
+    "    o(t_1,x) <= o(t_2,x)      forall (t_1<t_2),x",
+};
+
+const CdoHelp WindTransHelp = {
+    "NAME",
+    "    uvDestag, rotuvNorth, projuvLatLon - Wind Transformation",
+    "",
+    "SYNOPSIS",
+    "    uvDestag,u,v[,-/+0.5[,-/+0.5]]  infile outfile",
+    "    rotuvNorth,u,v  infile outfile",
+    "    projuvLatLon,u,v  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains special operators for datsets with wind components on a rotated lon/lat grid, ",
+    "    e.g. data from the regional model HIRLAM or REMO. ",
+    "",
+    "OPERATORS",
+    "    uvDestag      Destaggering of u/v wind components",
+    "                  This is a special operator for destaggering of wind components.",
+    "                  If the file contains a grid with temperature (name='t' or code=11)",
+    "                  then grid_temp will be used for destaggered wind.",
+    "    rotuvNorth    Rotate u/v wind to North pole.",
+    "                  This is an operator for transformation of wind-vectors from grid-relative to north-pole",
+    "                  relative for the whole file. (FAST implementation with JACOBIANS)",
+    "    projuvLatLon  Cylindrical Equidistant projection",
+    "                  Thus is an operator for transformation of wind-vectors from the globe-spherical coordinate system",
+    "                  into a flat Cylindrical Equidistant (lat-lon) projection. (FAST JACOBIAN implementation)",
+    "",
+    "PARAMETER",
+    "    u,v            STRING  Pair of u,v wind components (use variable names or code numbers)",
+    "    -/+0.5,-/+0.5  STRING  Destaggered grid offsets are optional (default -0.5,-0.5)",
+};
+
+const CdoHelp RotuvbHelp = {
+    "NAME",
+    "    rotuvb - Rotation",
+    "",
+    "SYNOPSIS",
+    "    rotuvb,u,v,...  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This is a special operator for datsets with wind components on a rotated grid, ",
+    "    e.g. data from the regional model REMO. It performs a backward transformation of ",
+    "    velocity components U and V from a rotated spherical system to a geographical system.",
+    "",
+    "PARAMETER",
+    "    u,v,...  STRING  Pairs of zonal and meridional velocity components (use variable names or code numbers)",
+    "",
+    "NOTE",
+    "    This is a specific implementation for data from the REMO model, it may not work with data from other sources.",
+};
+
+const CdoHelp MrotuvbHelp = {
+    "NAME",
+    "    mrotuvb - Backward rotation of MPIOM data",
+    "",
+    "SYNOPSIS",
+    "    mrotuvb  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    MPIOM data are on a rotated Arakawa C grid. The velocity components U and V are located on",
+    "    the edges of the cells and point in the direction of the grid lines and rows.",
+    "    With mrotuvb the velocity vector is rotated in latitudinal and longitudinal direction.",
+    "    Before the rotation, U and V are interpolated to the scalar points (cell center).",
+    "    U is located with the coordinates for U in infile1 and V in infile2.",
+    "    mrotuvb assumes a positive meridional flow for a flow from grid point(i,j) to grid point(i,j+1)",
+    "    and positive zonal flow for a flow from grid point(i+1,j) to point(i,j).",
+    "",
+    "NOTE",
+    "    This is a specific implementation for data from the MPIOM model, it may not work with data from other sources.",
+};
+
+const CdoHelp MastrfuHelp = {
+    "NAME",
+    "    mastrfu - Mass stream function",
+    "",
+    "SYNOPSIS",
+    "    mastrfu  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This is a special operator for the post processing of the atmospheric general circulation",
+    "    model ECHAM. It computes the mass stream function (code=272). The input dataset have ",
+    "    to be a zonal mean of v-velocity [m/s] (code=132) on pressure levels.",
+};
+
+const CdoHelp PressureHelp = {
+    "NAME",
+    "    pressure_half, pressure, delta_pressure - Pressure on model levels",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators to calculate the pressure on model levels.",
+    "    To calculate the pressure on model levels, the a and b coefficients defining the model levels and",
+    "    the surface pressure are required. The a and b coefficients are normally part of the model level data.",
+    "    If not available, the surface pressure can be derived from the logarithm of the surface pressure.",
+    "    The surface pressure is identified by the GRIB1 code number or NetCDF CF standard name.",
+    "    ",
+    "     Name                       & Units      & GRIB1 code & CF standard name",
+    "       log surface pressure     &  Pa        &  152       &",
+    "       surface pressure         &  Pa        &  134       &  surface_air_pressure",
+    "    ",
+    "",
+    "OPERATORS",
+    "    pressure_half   Pressure on half-levels",
+    "                    This operator computes the pressure on model half-levels in pascal.",
+    "                    The model half-level pressure (p_half) is given by:",
+    "                    ",
+    "                    ",
+    "                            p_half = a + b ∗ sp",
+    "                    ",
+    "                    with",
+    "                       a, b: coefficients defining the model levels",
+    "                       sp: surface pressure",
+    "    pressure        Pressure on full-levels",
+    "                    This operator computes the pressure on model full-levels in pascal.",
+    "                    The pressure on model full-levels (p_full) is in the middle of the layers defined by the model half-levels:",
+    "                    ",
+    "                            p_full = (p_half_above + p_half_below) / 2",
+    "                    ",
+    "    delta_pressure  Pressure difference of half-levels",
+    "                    This operator computes the pressure difference between to model half-levels.",
+    "                    ",
+    "                            delta_p = p_half_below - p_half_above",
+    "                    ",
+};
+
+const CdoHelp DeriveparHelp = {
+    "NAME",
+    "    sealevelpressure, gheight, gheight_half - Derived model parameters",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains operators that calculate derived model parameters. These are currently the parameters",
+    "    sea level pressure and geopotential height. All necessary input variables are identified by their GRIB1",
+    "    code number or the NetCDF CF standard name.",
+    "    Supported GRIB1 parameter tables are: WMO standard table number 2 and ECMWF local table number 128.",
+    "    ",
+    "     CF standard name            & Units      & GRIB 1 code      ",
+    "       surface_air_pressure      &  Pa        &  134",
+    "       air_temperature           &  K         &  130",
+    "       specific_humidity         &  kg/kg     &  133",
+    "       surface_geopotential      &  m2 s-2    &  129",
+    "       geopotential_height       &  m         &  156",
+    "    ",
+    "",
+    "OPERATORS",
+    "    sealevelpressure  Sea level pressure",
+    "                      This operator computes the sea level pressure (air_pressure_at_sea_level). Required input fields",
+    "                      are surface_air_pressure, surface_geopotential and air_temperature on full hybrid sigma pressure levels.",
+    "    gheight           Geopotential height on full-levels",
+    "                      This operator computes the geopotential height (geopotential_height) on model full-levels in metres.",
+    "                      Required input fields are surface_air_pressure, surface_geopotential, specific_humidity and air_temperature",
+    "                      on full hybrid sigma pressure levels. Note, this procedure is an approximation, which doesn't take into",
+    "                      account the effects of e.g. cloud ice and water, rain and snow.",
+    "    gheight_half      Geopotential height on half-levels",
+    "                      This operator computes the geopotential height (geopotential_height) on model half-levels in metres.",
+    "                      Required input fields are surface_air_pressure, surface_geopotential, specific_humidity and air_temperature",
+    "                      on full hybrid sigma pressure levels. Note, this procedure is an approximation, which doesn't take into",
+    "                      account the effects of e.g. cloud ice and water, rain and snow.",
+};
+
+const CdoHelp AdisitHelp = {
+    "NAME",
+    "    adisit, adipot - Potential temperature to in-situ temperature and vice versa",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,pressure]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "",
+    "OPERATORS",
+    "    adisit  Potential temperature to in-situ temperature",
+    "            This is a special operator for the post processing of the ocean and sea ice model MPIOM.",
+    "            It converts potential temperature adiabatically to in-situ temperature to(t, s, p).",
+    "            Required input fields are sea water potential temperature (name=tho; code=2) and sea water salinity (name=sao; code=5).",
+    "            Pressure is calculated from the level information or can be specified by the optional parameter.",
+    "            Output fields are sea water temperature (name=to; code=20) and sea water salinity (name=s; code=5).",
+    "    adipot  In-situ temperature to potential temperature",
+    "            This is a special operator for the post processing of the ocean and sea ice model MPIOM.",
+    "            It converts in-situ temperature to potential temperature tho(to, s, p). Required input fields",
+    "            are sea water in-situ temperature (name=t; code=2) and sea water salinity (name=sao,s; code=5).",
+    "            Pressure is calculated from the level information or can be specified by the optional parameter.",
+    "            Output fields are sea water temperature (name=tho; code=2) and sea water salinity (name=s; code=5).",
+    "",
+    "PARAMETER",
+    "    pressure  FLOAT   Pressure in bar (constant value assigned to all levels)",
+};
+
+const CdoHelp RhopotHelp = {
+    "NAME",
+    "    rhopot - Calculates potential density",
+    "",
+    "SYNOPSIS",
+    "    rhopot[,pressure]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This is a special operator for the post processing of the ocean and sea ice model MPIOM.",
+    "    It calculates the sea water potential density (name=rhopoto; code=18). Required input fields ",
+    "    are sea water in-situ temperature (name=to; code=20) and sea water salinity (name=sao; code=5).",
+    "    Pressure is calculated from the level information or can be specified by the optional parameter.",
+    "",
+    "PARAMETER",
+    "    pressure  FLOAT   Pressure in bar (constant value assigned to all levels)",
+};
+
+const CdoHelp HistogramHelp = {
+    "NAME",
+    "    histcount, histsum, histmean, histfreq - Histogram",
+    "",
+    "SYNOPSIS",
+    "    <operator>,bounds  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module creates bins for a histogram of the input data.",
+    "    The bins have to be adjacent and have non-overlapping intervals.",
+    "    The user has to define the bounds of the bins. The first value",
+    "    is the lower bound and the second value the upper bound of the",
+    "    first bin. The bounds of the second bin are defined by the",
+    "    second and third value, aso.",
+    "    Only 2-dimensional input fields are allowed. The output file ",
+    "    contains one vertical level for each of the bins requested.",
+    "",
+    "OPERATORS",
+    "    histcount  Histogram count",
+    "               Number of elements in the bin range.",
+    "    histsum    Histogram sum",
+    "               Sum of elements in the bin range.",
+    "    histmean   Histogram mean",
+    "               Mean of elements in the bin range.",
+    "    histfreq   Histogram frequency",
+    "               Relative frequency of elements in the bin range.",
+    "",
+    "PARAMETER",
+    "    bounds  FLOAT  Comma-separated list of the bin bounds (-inf and inf valid)",
+};
+
+const CdoHelp SethaloHelp = {
+    "NAME",
+    "    sethalo - Set the bounds of a field",
+    "",
+    "SYNOPSIS",
+    "    sethalo[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator sets the boundary in the east, west, south and north of the rectangular understood fields.",
+    "    Positive values of the parameters increase the boundary in the selected direction. Negative values",
+    "    decrease the field at the selected boundary. The new rows and columns are filled with the missing value.",
+    "    With the optional parameter value a different fill value can be used. Global cyclic fields are filled",
+    "    cyclically at the east and west borders, if the fill value is not set by the user.",
+    "",
+    "PARAMETER",
+    "    east   INTEGER  East halo",
+    "    west   INTEGER  West halo",
+    "    south  INTEGER  South halo",
+    "    north  INTEGER  North halo",
+    "    value  FLOAT    Fill value (default is the missing value)",
+};
+
+const CdoHelp WctHelp = {
+    "NAME",
+    "    wct - Windchill temperature",
+    "",
+    "SYNOPSIS",
+    "    wct  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 and infile2 be time series of temperature and wind",
+    "    speed records, then a corresponding time series of resulting windchill",
+    "    temperatures is written to outfile. The wind chill temperature",
+    "    calculation is only valid for a temperature of T <= 33 °C and a wind speed",
+    "    of v >= 1.39 m/s. Whenever these conditions are not satisfied, a missing",
+    "    value is written to outfile. Note that temperature and wind speed records",
+    "    have to be given in units of °C and m/s, respectively.",
+};
+
+const CdoHelp FdnsHelp = {
+    "NAME",
+    "    fdns - Frost days where no snow index per time period",
+    "",
+    "SYNOPSIS",
+    "    fdns  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily minimum temperature TN",
+    "    and infile2 be a corresponding series of daily surface snow",
+    "    amounts. Then the number of days where TN < 0 °C and the surface ",
+    "    snow amount is less than 1 cm is counted. The temperature TN",
+    "    have to be given in units of Kelvin.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile.",
+};
+
+const CdoHelp StrwinHelp = {
+    "NAME",
+    "    strwin - Strong wind days index per time period",
+    "",
+    "SYNOPSIS",
+    "    strwin[,v]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum horizontal wind speed",
+    "    VX, then the number of days where VX > v is counted. The horizontal wind",
+    "    speed v is an optional parameter with default v = 10.5 m/s. A further",
+    "    output variable is the maximum number of consecutive days with maximum wind",
+    "    speed greater than or equal to v. Note that both VX and v have to be given in",
+    "    units of m/s. Also note that the horizontal wind speed is defined as the",
+    "    square root of the sum of squares of the zonal and meridional wind speeds.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    v  FLOAT   Horizontal wind speed threshold (m/s, default v = 10.5 m/s)",
+};
+
+const CdoHelp StrbreHelp = {
+    "NAME",
+    "    strbre - Strong breeze days index per time period",
+    "",
+    "SYNOPSIS",
+    "    strbre  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum horizontal wind speed",
+    "    VX, then the number of days where VX is greater than or equal to 10.5 m/s ",
+    "    is counted. A further output variable is the maximum number of consecutive",
+    "    days with maximum wind speed greater than or equal to 10.5 m/s. Note that",
+    "    VX is defined as the square root of the sum of squares of the zonal and",
+    "    meridional wind speeds and have to be given in units of m/s.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile.",
+};
+
+const CdoHelp StrgalHelp = {
+    "NAME",
+    "    strgal - Strong gale days index per time period",
+    "",
+    "SYNOPSIS",
+    "    strgal  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum horizontal wind speed",
+    "    VX, then the number of days where VX is greater than or equal to 20.5 m/s ",
+    "    is counted. A further output variable is the maximum number of consecutive",
+    "    days with maximum wind speed greater than or equal to 20.5 m/s. Note that",
+    "    VX is defined as the square root of the sum of square of the zonal and",
+    "    meridional wind speeds and have to be given in units of m/s.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile.",
+};
+
+const CdoHelp HurrHelp = {
+    "NAME",
+    "    hurr - Hurricane days index per time period",
+    "",
+    "SYNOPSIS",
+    "    hurr  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum horizontal wind speed",
+    "    VX, then the number of days where VX is greater than or equal to 32.5 m/s",
+    "    is counted. A further output variable is the maximum number of consecutive",
+    "    days with maximum wind speed greater than or equal to 32.5 m/s. Note that",
+    "    VX is defined as the square root of the sum of squares of the zonal and",
+    "    meridional wind speeds and have to be given in units of m/s.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile.",
+};
+
+const CdoHelp CMORliteHelp = {
+    "NAME",
+    "    cmorlite - CMOR lite",
+    "",
+    "SYNOPSIS",
+    "    cmorlite,table[,convert]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    The CMOR (Climate Model Output Rewriter) library comprises a set of",
+    "    functions, that can be used to produce CF-compliant NetCDF files that ",
+    "    fulfill the requirements of many of the climate community's standard",
+    "    model experiments. These experiments are collectively referred to as",
+    "    MIP's. Much of the metadata written to the output files is defined in",
+    "    MIP-specific tables, typically made available from each MIP's web site.",
+    "    ",
+    "    The CDO operator cmorlite process the header and variable section",
+    "    of such MIP tables and writes the result with the internal IO library CDI.",
+    "    In addition to the CMOR 2 and 3 table format, the CDO parameter table format",
+    "    is also supported. The following parameter table entries are available:",
+    "    ",
+    "     Entry           & Type        & Description      ",
+    "     name            & WORD        & Name of the variable",
+    "     out_name        & WORD        & New name of the variable",
+    "     type            & WORD        & Data type (real or double)",
+    "     standard_name   & WORD        & As defined in the CF standard name table",
+    "     long_name       & STRING      & Describing the variable",
+    "     units           & STRING      & Specifying the units for the variable",
+    "     comment         & STRING      & Information concerning the variable",
+    "     cell_methods    & STRING      & Information concerning calculation of means or climatologies",
+    "     cell_measures   & STRING      & Indicates the names of the variables containing cell areas and volumes",
+    "     missing_value   & FLOAT       & Specifying how missing data will be identified",
+    "     valid_min       & FLOAT       & Minimum valid value",
+    "     valid_max       & FLOAT       & Maximum valid value",
+    "     ok_min_mean_abs & FLOAT       & Minimum absolute mean",
+    "     ok_max_mean_abs & FLOAT       & Maximum absolute mean",
+    "     factor          & FLOAT       & Scale factor",
+    "     delete          & INTEGER     & Set to 1 to delete variable",
+    "     convert         & INTEGER     & Set to 1 to convert the unit if necessary",
+    "    ",
+    "    Most of the above entries are stored as variables attributes, some of them are handled differently.",
+    "    The variable name is used as a search key for the parameter table. valid_min, valid_max,",
+    "    ok_min_mean_abs and ok_max_mean_abs are used to check the range of the data.",
+    "",
+    "PARAMETER",
+    "    table    STRING   Name of the CMOR table as specified from PCMDI",
+    "    convert  STRING   Converts the units if necessary",
+};
+
+const CdoHelp VerifygridHelp = {
+    "NAME",
+    "    verifygrid - Verify grid coordinates",
+    "",
+    "SYNOPSIS",
+    "    verifygrid  infile",
+    "",
+    "DESCRIPTION",
+    "    This operator verifies the coordinates of all horizontal grids found in infile.",
+    "    Among other things, it searches for duplicate cells, non-convex cells,",
+    "    and whether the center is located outside the cell bounds.",
+    "    Use the CDO option -v to output the position of these cells.",
+    "    This information can be useful to avoid problems when interpolating the data.",
+};
+
+const CdoHelp HealpixHelp = {
+    "NAME",
+    "    hpdegrade, hpupgrade - Change healpix resolution",
+    "",
+    "SYNOPSIS",
+    "    <operator>,parameter  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Degrade or upgrade the resolution of a healpix grid.",
+    "",
+    "OPERATORS",
+    "    hpdegrade  Degrade healpix",
+    "               Degrade the resolution of a healpix grid. The value of the target pixel is the mean of the source pixels.",
+    "    hpupgrade  Upgrade healpix",
+    "               Upgrade the resolution of a healpix grid. The values of the target pixels is the value of the source pixel.",
+    "",
+    "PARAMETER",
+    "    nside  INTEGER The nside of the target healpix, must be a power of two [default: same as input].",
+    "    order  STRING  Pixel ordering of the target healpix ('nested' or 'ring').",
+    "    power  FLOAT   If non-zero, divide the result by (nside[in]/nside[out])**power. power=-2 keeps the sum of the map invariant.",
+};
+
+const CdoHelp NCL_windHelp = {
+    "NAME",
+    "    uv2vr_cfd, uv2dv_cfd - Wind transformation",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,u,v,boundOpt,outMode]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module contains CDO operators with an interface to NCL functions.",
+    "    The corresponding NCL functions have the same name. A more detailed description",
+    "    of those NCL function can be found on the NCL homepage https://www.ncl.ucar.edu.",
+    "",
+    "OPERATORS",
+    "    uv2vr_cfd  U and V wind to relative vorticity",
+    "               Computes relative vorticity for a latitude-longitude grid using centered finite differences.",
+    "               The grid need not be global and missing values are allowed.",
+    "    uv2dv_cfd  U and V wind to divergence",
+    "               Computes divergence for a latitude-longitude grid using centered finite differences.",
+    "               The grid need not be global and missing values are allowed.",
+    "",
+    "PARAMETER",
+    "    u         STRING   Name of variable u (default: u)",
+    "    v         STRING   Name of variable v (default: v)",
+    "    boundOpt  INTEGER  Boundary condition option (0-3) (default: 0/1 for cyclic grids)",
+    "    outMode   STRING   Output mode new/append (default: new)",
+};
+
+const CdoHelp CMORHelp = {
+    "NAME",
+    "    cmor - Climate Model Output Rewriting to produce CMIP-compliant data",
+    "",
+    "SYNOPSIS",
+    "    cmor,MIPtable[,cmor_name=VarList[,key=value[,...]]]  infile",
+    "",
+    "DESCRIPTION",
+    "    ",
+    "    ",
+    "    The CDO operator cmor converts an infile into a CMIP-compliant format",
+    "    by using the CMOR library. Each output file contains a single output variable.",
+    "    The name of the output files are generated by CMOR according to a template based on the",
+    "    DRS (Data reference Syntax) of the project. CMOR checks and applies the information delivered",
+    "    through the project dependend MIPtable on the infile. Additional information",
+    "    which is required for the conversion can be configured via keyvalues as optional parameters.",
+    "    ",
+    "    By specifying a variable selector keyvalue, e.g. cmor_name=tas, the user can",
+    "    pre-select a subset of infile variables. If name or code is specified, a",
+    "    corresponding cmor_name which can also be found in the MIPtable is also",
+    "    required to map the infile variable to the CMOR-variable. For mapping more",
+    "    variables at the operator call, one can specify a mapping table via keyword mapping_table.",
+    "    ",
+    "    Global attributes must be collected in info files and can be specified via keyword",
+    "    info. All required and optional global attributes as well as information",
+    "    about table file formats are given in the 'cdo cmor manual'.",
+    "    ",
+    "    If questions remain, do not hesitate to ask and send an email to wachsmannATdkrz.de.",
+    "    ",
+    "",
+    "PARAMETER",
+    "    MIPtable                   STRING    Name of the MIP table as used by CMOR.",
+    "                               --------------------------------------------------------------------------------------------",
+    "    cmor_name           | cn   STRING    Variable selector and specified in the MIP table.",
+    "                                         Comma-separated list of CMOR variable names.",
+    "                                         Default is to process all variables.",
+    "    name                | n    STRING    Variable selector.",
+    "                                         Name of a selected @file{infile} variable.",
+    "    code                | c    INTEGER   Variable selector. ",
+    "                                         Three digits (GRIB) Code of a selected @file{infile} variable.",
+    "                               --------------------------------------------------------------------------------------------",
+    "    info                | i    STRING    Preprozessing.",
+    "                                         Comma-separated list of filenames.",
+    "                                         Containins global attributes and control keywords.",
+    "                                         Default: CWD/.cdocmorinfo",
+    "    grid_info           | gi   STRING    Preprozessing.",
+    "                                         NetCDF or table formatted file with model grid description.",
+    "                                         Horizontal and vertical axes are substituted with the ones from grid info file.",
+    "    mapping_table       | mt   STRING    Preprozessing.",
+    "                                         Fortran Namelist containing variable information for e.g. renaming.",
+    "    keep_all_attributes | kaa  STRING    Preprozessing.",
+    "                                         'y' for passing all infile attributes. 'n' for discarding all infile attributes.",
+    "                               --------------------------------------------------------------------------------------------",
+    "    drs                 | d    CHARACTER Output control.",
+    "                                         Do(=y, default) or do not(=n) move output into the project DRS structure.",
+    "    drs_root            | dr   STRING    Output control. CMOR output root directory.",
+    "                                         Default: CWD.",
+    "    output_mode         | om   CHARACTER Output control.",
+    "                                         Either 'r' for replace (default) or 'a' for append mode.",
+    "    last_chunk          | lc   STRING    Output control. Filename of chunk to which shall be appended.  ",
+    "    max_size            | ms   INTEGER   Output control. Limit of output file sie in GigaByte.",
+    "    deflate_level       | dl   INTEGER   Output control. Compression level. -1: No compression. 0: Only shuffle.",
+    "    version_date        | vd   INTEGER   Output control. Subdirectory name in CMIP6 DRS.",
+    "                               --------------------------------------------------------------------------------------------",
+    "    required_time_units | rtu  STRING    Temporal description.",
+    "                                         Time axis reference date specified by the experiment.",
+    "                                         Format: 'days since YYYY-day-month hh:mm:ss'.",
+    "    cell_methods        | cm   CHARACTER Temporal description.",
+    "                                         Cell_methods of time axis.",
+    "                                         Value is one of 'm' (default)  , 'p', 'c', 'n', 'd'",
+    "                               --------------------------------------------------------------------------------------------",
+    "    units               | u    STRING    Variable attrbiute. Units of the variable.",
+    "                                         Must be known by library UDunits.",
+    "    variable_comment    | vc   STRING    Variable attribute. Variable comment.",
+    "    positive            | p    CHARACTER Variable attrbiute.",
+    "                                         Positive flux direction, either 'u' for upward or 'd' for downward.",
+    "    z_axis              | za   STRING    Name of the coordinate variable associated with",
+    "                                         the z-axis of the target variable.",
+    "    character_axis      | ca   STRING    Name of the coordinate variable associated with",
+    "                                         a character axis of the target variable.",
+    "                                         Valid axes names are: basin, vegtype or oline. ",
+    "    t_axis              | ta   STRING    Sets time values and time bounds to the nearest value",
+    "                                         required by the project given by the value of t_axis.",
+    "                                         Valid value is: cmip",
+};
+
+const CdoHelp MagplotHelp = {
+    "NAME",
+    "    contour, shaded, grfill - Lat/Lon plot",
+    "",
+    "SYNOPSIS",
+    "    <operator>,parameter  infile obase",
+    "",
+    "DESCRIPTION",
+    "    The operators in this module generates 2D Lon/Lat plots.",
+    "    The data for the plot is read from infile.",
+    "    Only data on rectilinear Lon/Lat grids are supported.",
+    "    The output file will be named <obase>_<param>.<device> where param is the parameter name and",
+    "    device is the device name. The default output file format is postscript,",
+    "    this can be changed with the device parameter.",
+    "    The type of the plot depends on the choosen operator.",
+    "    ",
+    "    Here is a list of all common plot parameters:",
+    "    ",
+    "     Keyname     & Type    & Description      ",
+    "     device      & STRING  & Output device (ps, eps, pdf, png, gif, gif_animation, jpeg, svg, kml)",
+    "     projection  & STRING  & Projection (cylindrical, polar_stereographic, robinson, mercator)",
+    "     style       & STRING  & Contour line style (solid, dash, dot, chain_dash, chain_dot)",
+    "     min         & FLOAT   & Minimum value",
+    "     max         & FLOAT   & Maximum value",
+    "     lon_max     & FLOAT   & Maximum longitude of the image",
+    "     lon_min     & FLOAT   & Minimum longitude of the image",
+    "     lat_max     & FLOAT   & Maximum latitude of the image",
+    "     lat_min     & FLOAT   & Minimum latitude of the image",
+    "     count       & INTEGER & Number of Contour levels / Colour bands  ",
+    "     interval    & FLOAT   & Interval in data units between two bands lines",
+    "     list        & INTEGER & List of levels to be plotted",
+    "     RGB         & STRING  & TRUE or FALSE, to  indicate, if the input colour is in RGB format",
+    "     step_freq   & INTEGER & Frequency of time steps to be considered for making the animation",
+    "                 &         & (device=gif_animation). Default value is \"1\" (all time steps).",
+    "                 &         & Will be ignored if input file has multiple variables.",
+    "     file_split  & STRING  & TRUE or FALSE, to split the output file for each variable, if input has",
+    "                 &         & multiple variables. Default value is \"FALSE\". Valid only for \"PS\" format.",
+    "",
+    "OPERATORS",
+    "    contour  Contour plot",
+    "             The operator contour generates the discrete contour lines of the input field values.",
+    "             The following additional parameters are valid for contour operator,",
+    "             module in addition to the common plot parameters:",
+    "             ",
+    "              Keyname      & Type    & Description      ",
+    "              colour       & STRING  & Colour for drawing the contours",
+    "              thickness    & FLOAT   & Thickness of the contour line",
+    "              style        & STRING  & Line Style can be \"SOLID\", \"DASH\", \"DOT\", \"CHAIN_DASH\",",
+    "                           &         & \"CHAIN_DOT\"",
+    "    shaded   Shaded contour plot",
+    "             The operator shaded generates the filled contours of the given input field values.",
+    "             The following additional parameters are valid for shaded contour and gridfill operator,",
+    "             in addition to the common plot parameters.",
+    "             ",
+    "              Keyname      & Type    & Description      ",
+    "              colour_min   & STRING  & Colour for the Minimum colour band",
+    "              colour_max   & STRING  & Colour for the Minimum colour band",
+    "              colour_triad & STRING  & Direction of colour sequencing for shading \"CW\" or \"ACW\",",
+    "                           &         & to denote \"clockwise\" and \"anticlockwise\" respectively.",
+    "                           &         & To be used in conjunction with \"colour_min\", \"colour_max\"",
+    "                           &         & options. Default is \"ACW\"",
+    "              colour_table & STRING  & File with user specified colours with the format as",
+    "             ",
+    "             Example file for 6 colours in RGB format:",
+    "             	6",
+    "             	RGB(0.0;0.0;1.0)",
+    "             	RGB(0.0;0.0;0.5)",
+    "             	RGB(0.0;0.5;0.5)",
+    "             	RGB(0.0;1.0;0.0)",
+    "             	RGB(0.5;0.5;0.0)",
+    "             	RGB(1.0;0.0;0.0)",
+    "             ",
+    "    grfill   Shaded gridfill plot",
+    "             The operator grfill is similar to satellite imaging and shades each cell (pixel) according",
+    "             to the value of the field at that cell.",
+    "",
+    "PARAMETER",
+    "    parameter  STRING   Comma-separated list of plot parameters",
+    "",
+    "NOTE",
+    "    All colour parameter can be either standard name or in RGB format.",
+    "    The valid standard name strings for \"colour\" are:",
+    "    ",
+    "    \"red\", \"green\", \"blue\", \"yellow\", \"cyan\", \"magenta\", \"black\", \"avocado\", \"beige\",",
+    "    \"brick\", \"brown\", \"burgundy\", \"charcoal\", \"chestnut\", \"coral\", \"cream\", \"evergreen\",",
+    "    \"gold\", \"grey\", \"khaki\", \"kellygreen\", \"lavender\", \"mustard\", \"navy\", \"ochre\",",
+    "    \"olive\", \"peach\", \"pink\", \"rose\", \"rust\", \"sky\", \"tan\", \"tangerine\", \"turquoise\",",
+    "    \"violet\", \"reddishpurple\", \"purplered\", \"purplishred\", \"orangishred\", \"redorange\",",
+    "    \"reddishorange\", \"orange\", \"yellowishorange\", \"orangeyellow\", \"orangishyellow\",",
+    "    \"greenishyellow\", \"yellowgreen\", \"yellowishgreen\", \"bluishgreen\", \"bluegreen\",",
+    "    \"greenishblue\", \"purplishblue\", \"bluepurple\", \"bluishpurple\", \"purple\", \"white\"",
+};
+
+const CdoHelp MagvectorHelp = {
+    "NAME",
+    "    vector - Lat/Lon vector plot",
+    "",
+    "SYNOPSIS",
+    "    vector,parameter  infile obase",
+    "",
+    "DESCRIPTION",
+    "    This operator generates 2D Lon/Lat vector plots.",
+    "    The data for the plot is read from infile. The input is expected to contain two velocity",
+    "    components. Only data on rectilinear Lon/Lat grids are supported.",
+    "    The output file will be named <obase>.<device> where device is the device name. ",
+    "    The default output file format is postscript, this can be changed with the device parameter.",
+    "    ",
+    "    Here is a list of all vector plot parameters:",
+    "    ",
+    "     Keyname     & Type    & Description      ",
+    "     device      & STRING  & Output device (ps, eps, pdf, png, gif, gif_animation, jpeg, svg, kml)",
+    "     projection  & STRING  & Projection (cylindrical, polar_stereographic, robinson, mercator)",
+    "     thin_fac    & FLOAT   & Controls the actual number of wind arrows or flags plotted (default 2).",
+    "     unit_vec    & FLOAT   & Wind speed in m/s represented by a unit vector (1.0cm)",
+    "     step_freq   & INTEGER & Frequency of time steps to be considered for making the animation",
+    "                 &         & (device=gif_animation). Default value is \"1\" (all time steps).",
+    "                 &         & Will be ignored if input file has multiple variables.",
+    "",
+    "PARAMETER",
+    "    parameter  STRING   Comma-separated list of plot parameters",
+};
+
+const CdoHelp MaggraphHelp = {
+    "NAME",
+    "    graph - Line graph plot",
+    "",
+    "SYNOPSIS",
+    "    graph,parameter  infiles outfile",
+    "",
+    "DESCRIPTION",
+    "    This operator generates line graph plots.",
+    "    The data for the plot is read from infiles. The result is written to outfile.",
+    "    The default output file format is postscript, this can be changed with the device parameter.",
+    "    ",
+    "    Here is a list of all graph plot parameters:",
+    "    ",
+    "     Keyname    & Type    & Description      ",
+    "     device     & STRING  & Output device (ps, eps, pdf, png, gif, gif_animation, jpeg, svg, kml)",
+    "     ymin       & FLOAT   & Minimum value of the y-axis data ",
+    "     ymax       & FLOAT   & Maximum value of the y-axis data ",
+    "     linewidth  & INT     & Linewidth (default 8)",
+    "     stat       & STRING  & \"TRUE\" or \"FALSE\", to switch on the mean computation. Default is \"FALSE\".",
+    "                &         & Will be overridden to \"FALSE\", if input files have unequal number of time",
+    "                &         & steps or different start/end times. ",
+    "     sigma      & FLOAT   & Standard deviation value for generating shaded back ground around the mean value.",
+    "                &         & To be used in conjunction with 'stat=\"TRUE\"' ",
+    "     obsv       & STRING  & To indicate if the input files have an observation data, by setting to \"TRUE\".",
+    "                &         & Default value is \"FALSE\". The observation data should be the first file in the",
+    "                &         & input file list. The observation data is always plotted in black colour. ",
+    "",
+    "PARAMETER",
+    "    parameter  STRING   Comma-separated list of plot parameters",
+};
+
+const CdoHelp EcaCddHelp = {
+    "NAME",
+    "    eca_cdd, etccdi_cdd - Consecutive dry days index per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,R[,N[,params]]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily precipitation amount RR, then the largest number ",
+    "    of consecutive days where RR is less than R is counted. R is an optional parameter with ",
+    "    default R = 1 mm. A further output variable is the number of dry periods of more than N days.",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_cdd     Consecutive dry days index per time period",
+    "                The operator counts over the entire time series.",
+    "                The date information of a timestep in outfile is the date of",
+    "                the last contributing timestep in infile.",
+    "    etccdi_cdd  Consecutive dry days index per time period",
+    "                The default output frequency is yearly.",
+    "                Periods within overlapping years are accounted for the first year.",
+    "                The date information of a timestep in outfile is the mid of",
+    "                the frequency interval.",
+    "",
+    "PARAMETER",
+    "    R     FLOAT    Precipitation threshold (unit: mm; default: R = 1 mm)",
+    "    N     INTEGER  Minimum number of days exceeded (default: N = 5)",
+    "    freq  STRING   Output frequency (year, month)",
+};
+
+const CdoHelp EcaCfdHelp = {
+    "NAME",
+    "    eca_cfd - Consecutive frost days index per time period",
+    "",
+    "SYNOPSIS",
+    "    eca_cfd[,N]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily minimum temperature TN, then the largest number of",
+    "    consecutive days where TN < 0 °C is counted. Note that TN have to be given in units of Kelvin.",
+    "    A further output variable is the number of frost periods of more than N days.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    N  INTEGER  Minimum number of days exceeded (default: N = 5)",
+};
+
+const CdoHelp EcaCsuHelp = {
+    "NAME",
+    "    eca_csu - Consecutive summer days index per time period",
+    "",
+    "SYNOPSIS",
+    "    eca_csu[,T[,N]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum temperature TX, then the largest number of consecutive",
+    "    days where TX > T is counted. The number T is an optional parameter with default T = 25°C.",
+    "    Note that TN have to be given in units of Kelvin, whereas T have to be given in degrees Celsius.",
+    "    A further output variable is the number of summer periods of more than N days.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    T  FLOAT    Temperature threshold (unit: °C; default: T = 25°C)",
+    "    N  INTEGER  Minimum number of days exceeded (default: N = 5)",
+};
+
+const CdoHelp EcaCwdHelp = {
+    "NAME",
+    "    eca_cwd, etccdi_cwd - Consecutive wet days index per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,params]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily precipitation amount RR, then the largest number ",
+    "    of consecutive days where RR is at least R is counted. R is an optional parameter with ",
+    "    default R = 1 mm. A further output variable is the number of wet periods of more than N days.",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_cwd     Consecutive wet days index per time period",
+    "                The operator counts over the entire time series.",
+    "                The date information of a timestep in outfile is the date of",
+    "                the last contributing timestep in infile.",
+    "    etccdi_cwd  Consecutive wet days index per time period",
+    "                The default output frequency is yearly.",
+    "                Periods within overlapping years are accounted for the first year.",
+    "                The date information of a timestep in outfile is the mid of",
+    "                the frequency interval.",
+    "",
+    "PARAMETER",
+    "    R     FLOAT    Precipitation threshold (unit: mm; default: R = 1 mm)",
+    "    N     INTEGER  Minimum number of days exceeded (default: N = 5)",
+    "    freq  STRING   Output frequency (year, month)",
+};
+
+const CdoHelp EcaCwdiHelp = {
+    "NAME",
+    "    eca_cwdi - Cold wave duration index wrt mean of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_cwdi[,nday[,T]]  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily minimum temperature TN, and let infile2 be the mean ",
+    "    TNnorm of daily minimum temperatures for any period used as reference. Then counted is the number of days",
+    "    where, in intervals of at least nday consecutive days, TN < TNnorm - T.",
+    "    The numbers nday and T are optional parameters with default nday = 6 and T = 5°C. ",
+    "    A further output variable is the number of cold waves longer than or equal to nday days.",
+    "    TNnorm is calculated as the mean of minimum temperatures of a five day window centred on each calendar day ",
+    "    of a given climate reference period. Note that both TN and TNnorm have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+    "",
+    "PARAMETER",
+    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
+    "    T     FLOAT    Temperature offset (unit: °C; default: T = 5°C)",
+};
+
+const CdoHelp EcaCwfiHelp = {
+    "NAME",
+    "    eca_cwfi, etccdi_csdi - ",
+    "    Cold-spell days index wrt 10th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,nday[,params]]  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily mean temperature TG, and infile2 be the 10th",
+    "    percentile TGn10 of daily mean temperatures for any period used as reference. ",
+    "    Then counted is the number of days where, in intervals of at least nday consecutive days,",
+    "    TG < TGn10. The number nday is an optional parameter with default nday = 6.",
+    "    A further output variable is the number of cold-spell periods longer than or equal to nday days.",
+    "    TGn10 is calculated as the 10th percentile of daily mean temperatures of a five day window ",
+    "    centred on each calendar day of a given climate reference period. Note that both TG and TGn10 ",
+    "    have to be given in the same units.",
+    "",
+    "OPERATORS",
+    "    eca_cwfi     Cold-spell days index wrt 10th percentile of reference period",
+    "                 The operator counts over the entire time series.",
+    "                 The date information of a timestep in outfile is the date of",
+    "                 the last contributing timestep in infile.",
+    "    etccdi_csdi  Cold-spell duration index",
+    "                 The default output frequency is yearly.",
+    "                 Periods within overlapping years are accounted for the first year.",
+    "                 The date information of a timestep in outfile is the mid of",
+    "                 the frequency interval.",
+    "",
+    "PARAMETER",
+    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
+    "    freq  STRING   Output frequency (year, month)",
+};
+
+const CdoHelp EcaEtrHelp = {
+    "NAME",
+    "    eca_etr - Intra-period extreme temperature range",
+    "",
+    "SYNOPSIS",
+    "    eca_etr  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 and infile2 be time series of thr maximum and minimum",
+    "    temperature TX and TN, respectively. Then the extreme temperature",
+    "    range is the difference of the maximum of TX and the minimum of TN.",
+    "    Note that TX and TN have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timesteps in infile1 and infile2.",
+};
+
+const CdoHelp EcaFdHelp = {
+    "NAME",
+    "    eca_fd, etccdi_fd - Frost days index per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily minimum temperature TN,",
+    "    then the number of days where TN < 0 °C is counted. Note",
+    "    that TN have to be given in units of Kelvin. Parameter is a",
+    "    comma-separated list of \"key=value\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_fd     Frost days index per time period",
+    "               The operator counts over the entire time series.",
+    "               The date information of a timestep in outfile is the date of",
+    "               the last contributing timestep in infile.",
+    "    etccdi_fd  Frost days index per time period",
+    "               The default output frequency is yearly.",
+    "               The date information of a timestep in outfile is the mid of",
+    "               the frequency interval.",
+    "",
+    "PARAMETER",
+    "    freq  STRING    Output frequency (year, month)",
+};
+
+const CdoHelp EcaGslHelp = {
+    "NAME",
+    "    eca_gsl - Thermal Growing season length index",
+    "",
+    "SYNOPSIS",
+    "    eca_gsl[,nday[,T[,fland]]]  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily mean temperature TG, and infile2 be a land-water mask.",
+    "    Within a period of 12 months, the thermal growing season length is officially defined as the number of days between:",
+    "    - first occurrence of at least nday consecutive days with TG > T",
+    "    - first occurrence of at least nday consecutive days with TG < T within the last 6 months",
+    "    On northern hemisphere, this period corresponds with the regular year, whereas on southern hemisphere, it starts ",
+    "    at July 1st. Please note, that this definition may lead to weird results concerning values TG = T: ",
+    "    In the first half of the period, these days do not contribute to the gsl, but they do within the second half.",
+    "    Moreover this definition could lead to discontinuous values in equatorial regions.",
+    "    ",
+    "    The numbers nday and T are optional parameter with default nday = 6 and T = 5°C. ",
+    "    The number fland is an optional parameter with default value fland = 0.5 and denotes the fraction of ",
+    "    a grid point that have to be covered by land in order to be included in the calculation. A further output variable ",
+    "    is the start day of year of the growing season. Note that TG have to be given in units of Kelvin, whereas T ",
+    "    have to be given in degrees Celsius.",
+    "    ",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    nday   INTEGER  Number of consecutive days (default: nday = 6)",
+    "    T      FLOAT    Temperature threshold (unit: °C; default: T = 5°C)",
+    "    fland  FLOAT    Land fraction threshold (default: fland = 0.5)",
+};
+
+const CdoHelp EcaHdHelp = {
+    "NAME",
+    "    eca_hd - Heating degree days per time period",
+    "",
+    "SYNOPSIS",
+    "    eca_hd[,T1[,T2]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily mean temperature TG, then the heating degree days ",
+    "    are defined as the sum of T1 - TG, where only values TG < T2 are considered. ",
+    "    If T1 and T2 are omitted, a temperature of 17°C is used for both parameters. ",
+    "    If only T1 is given, T2 is set to T1. Note that TG have to be given in units ",
+    "    of kelvin, whereas T1 and T2 have to be given in degrees Celsius.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    T1  FLOAT   Temperature limit (unit: °C; default: T1 = 17°C)",
+    "    T2  FLOAT   Temperature limit (unit: °C; default: T2 = T1)",
+};
+
+const CdoHelp EcaHwdiHelp = {
+    "NAME",
+    "    eca_hwdi - Heat wave duration index wrt mean of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_hwdi[,nday[,T]]  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily maximum temperature TX, and let infile2 be the mean ",
+    "    TXnorm of daily maximum temperatures for any period used as reference. Then counted is the number of days",
+    "    where, in intervals of at least nday consecutive days, TX > TXnorm + T.",
+    "    The numbers nday and T are optional parameters with default nday = 6 and T = 5°C. ",
+    "    A further output variable is the number of heat waves longer than or equal to nday days. ",
+    "    TXnorm is calculated as the mean of maximum temperatures of a five day window centred on each calendar day",
+    "    of a given climate reference period. Note that both TX and TXnorm have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+    "",
+    "PARAMETER",
+    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
+    "    T     FLOAT    Temperature offset (unit: °C; default: T = 5°C)",
+};
+
+const CdoHelp EcaHwfiHelp = {
+    "NAME",
+    "    eca_hwfi, etccdi_wsdi - ",
+    "    Warm spell days index wrt 90th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,params]  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily mean temperature TG, and ",
+    "    infile2 be the 90th percentile TGn90 of daily mean temperatures",
+    "    for any period used as reference. Then counted is the number of days",
+    "    where, in intervals of at least nday consecutive days, TG > TGn90. The",
+    "    number nday is an optional parameter with default nday = 6. A further",
+    "    output variable is the number of warm-spell periods longer than or",
+    "    equal to nday days. ",
+    "    TGn90 is calculated as the 90th percentile of daily mean temperatures of a five ",
+    "    day window centred on each calendar day of a given climate reference period.",
+    "    Note that both TG and TGn90 have to be given in the same units.",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_hwfi     Warm spell days index wrt 90th percentile of reference period",
+    "                 The operator counts over the entire time series.",
+    "                 The date information of a timestep in outfile is the date of",
+    "                 the last contributing timestep in infile.",
+    "    etccdi_wsdi  Warm Spell Duration Index",
+    "                 The default output frequency is yearly.",
+    "                 Periods within overlapping years are accounted for the first year.",
+    "                 The date information of a timestep in outfile is the mid of",
+    "                 the frequency interval.",
+    "",
+    "PARAMETER",
+    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
+    "    freq  STRING   Output frequency (year, month)",
+};
+
+const CdoHelp EcaIdHelp = {
+    "NAME",
+    "    eca_id, etccdi_id - Ice days index per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum temperature TX,",
+    "    then the number of days where TX < 0 °C is counted. Note",
+    "    that TX have to be given in units of Kelvin. Parameter is a",
+    "    comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_id     Ice days index per time period",
+    "               The operator counts over the entire time series.",
+    "               The date information of a timestep in outfile is the date of",
+    "               the last contributing timestep in infile.",
+    "    etccdi_id  Ice days index per time period",
+    "               The default output frequency is yearly.",
+    "               The date information of a timestep in outfile is the mid of",
+    "               the frequency interval.",
+    "",
+    "PARAMETER",
+    "    freq  STRING    Output frequency (year, month)",
+};
+
+const CdoHelp EcaR75pHelp = {
+    "NAME",
+    "    eca_r75p - Moderate wet days wrt 75th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_r75p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 75th percentile RRn75 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the percentage of wet days with RR > RRn75 is calculated. ",
+    "    RRn75 is calculated as the 75th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,75.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR75ptotHelp = {
+    "NAME",
+    "    eca_r75ptot - Precipitation percent due to R75p days",
+    "",
+    "SYNOPSIS",
+    "    eca_r75ptot  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 75th percentile RRn75 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn75 to the total ",
+    "    precipitation sum is calculated. ",
+    "    RRn75 is calculated as the 75th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,75.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR90pHelp = {
+    "NAME",
+    "    eca_r90p - Wet days wrt 90th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_r90p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 90th percentile RRn90 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the percentage of wet days with RR > RRn90 is calculated. ",
+    "    RRn90 is calculated as the 90th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,90.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR90ptotHelp = {
+    "NAME",
+    "    eca_r90ptot - Precipitation percent due to R90p days",
+    "",
+    "SYNOPSIS",
+    "    eca_r90ptot  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 90th percentile RRn90 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn90 to the total ",
+    "    precipitation sum is calculated. ",
+    "    RRn90 is calculated as the 90th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,90.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR95pHelp = {
+    "NAME",
+    "    eca_r95p - Very wet days wrt 95th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_r95p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 95th percentile RRn95 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the percentage of wet days with RR > RRn95 is calculated. ",
+    "    RRn95 is calculated as the 95th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,95.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR95ptotHelp = {
+    "NAME",
+    "    eca_r95ptot - Precipitation percent due to R95p days",
+    "",
+    "SYNOPSIS",
+    "    eca_r95ptot  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 95th percentile RRn95 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn95 to the total ",
+    "    precipitation sum is calculated. ",
+    "    RRn95 is calculated as the 95th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,95.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR99pHelp = {
+    "NAME",
+    "    eca_r99p - Extremely wet days wrt 99th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_r99p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 99th percentile RRn99 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the percentage of wet days with RR > RRn99 is calculated. ",
+    "    RRn99 is calculated as the 99th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,99.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaR99ptotHelp = {
+    "NAME",
+    "    eca_r99ptot - Precipitation percent due to R99p days",
+    "",
+    "SYNOPSIS",
+    "    eca_r99ptot  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
+    "    and infile2 be the 99th percentile RRn99 of the daily precipitation amount at wet days for any period ",
+    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn99 to the total ",
+    "    precipitation sum is calculated. ",
+    "    RRn99 is calculated as the 99th percentile of all wet days of a given climate reference period.",
+    "    Usually infile2 is generated by the operator ydaypctl,99.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaPdHelp = {
+    "NAME",
+    "    eca_pd, eca_r10mm, eca_r20mm, etccdi_r1mm - ",
+    "    Precipitation days index per time period",
+    "",
+    "SYNOPSIS",
+    "    eca_pd,x  infile outfile",
+    "    eca_r10mm  infile outfile",
+    "    eca_r20mm  infile outfile",
+    "    etccdi_r1mm[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily precipitation amount RR in [mm] (or alternatively in [kg m-2]),",
+    "    then the number of days where RR is at least x mm is counted. ",
+    "    eca_r10mm and eca_r20mm are specific ECA operators with a daily precipitation amount of 10 and 20 mm respectively.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile",
+    "    except for the etccdi operator. Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_pd       Precipitation days index per time period",
+    "                 Generic ECA operator with daily precipitation sum exceeding x mm.",
+    "    eca_r10mm    Heavy precipitation days index per time period",
+    "                 Specific ECA operator with daily precipitation sum exceeding 10 mm.",
+    "    eca_r20mm    Very heavy precipitation days index per time period",
+    "                 Specific ECA operator with daily precipitation sum exceeding 20 mm.",
+    "    etccdi_r1mm  Precipitation days index per time period",
+    "                 The default output frequency is yearly.",
+    "                 The date information of a timestep in outfile is the mid of",
+    "                 the frequency interval.",
+    "",
+    "PARAMETER",
+    "    x     FLOAT   Daily precipitation amount threshold in [mm]",
+    "    freq  STRING  Output frequency (year, month)",
+    "",
+    "NOTE",
+    "    Precipitation rates in [mm/s] have to be converted to precipitation amounts (multiply with 86400 s).",
+    "    Apart from metadata information the result of eca_pd,1 and eca_rr1 is the same.",
+};
+
+const CdoHelp EcaRr1Help = {
+    "NAME",
+    "    eca_rr1 - Wet days index per time period",
+    "",
+    "SYNOPSIS",
+    "    eca_rr1[,R]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily precipitation amount RR in [mm] (or alternatively in [kg m-2]), then",
+    "    the number of days where RR is at least R is counted. R is an optional parameter with default R = 1 mm. ",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    R  FLOAT   Precipitation threshold (unit: mm; default: R = 1 mm)",
+};
+
+const CdoHelp EcaRx1dayHelp = {
+    "NAME",
+    "    eca_rx1day, etccdi_rx1day - ",
+    "    Highest one day precipitation amount per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,parameter]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily precipitation amount RR,",
+    "    then the maximum of RR is written to outfile. If the optional",
+    "    parameter mode is set to 'm' the maximum daily precipitation",
+    "    amounts are determined for each month. ",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_rx1day     Highest one day precipitation amount per time period",
+    "                   The operator counts over the entire time series.",
+    "                   The date information of a timestep in outfile is the date of",
+    "                   the last contributing timestep in infile.",
+    "    etccdi_rx1day  Maximum 1-day Precipitation",
+    "                   The default output frequency is yearly.",
+    "                   The date information of a timestep in outfile is the mid of",
+    "                   the frequency interval.",
+    "",
+    "PARAMETER",
+    "    freq  STRING  Output frequency (year, month)",
+};
+
+const CdoHelp EcaRx5dayHelp = {
+    "NAME",
+    "    eca_rx5day, etccdi_rx5day - ",
+    "    Highest five-day precipitation amount per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,x[,params]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of 5-day precipitation totals RR, then the maximum of RR is written to outfile. ",
+    "    A further output variable is the number of 5 day period with precipitation totals greater than x mm, where x ",
+    "    is an optional parameter with default x = 50 mm.",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_rx5day     Highest five-day precipitation amount per time period",
+    "                   The operator counts over the entire time series.",
+    "                   The date information of a timestep in outfile is the date of",
+    "                   the last contributing timestep in infile.",
+    "    etccdi_rx5day  Highest five-day precipitation amount per time period",
+    "                   The default output frequency is yearly.",
+    "                   Periods within overlapping years are accounted for the first year.",
+    "                   The date information of a timestep in outfile is the mid of",
+    "                   the frequency interval.",
+    "",
+    "PARAMETER",
+    "    x     FLOAT   Precipitation threshold (unit: mm; default: x = 50 mm)",
+    "    freq  STRING  Output frequency (year, month)",
+};
+
+const CdoHelp EcaSdiiHelp = {
+    "NAME",
+    "    eca_sdii - Simple daily intensity index per time period",
+    "",
+    "SYNOPSIS",
+    "    eca_sdii[,R]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily precipitation amount RR, then the mean precipitation amount at ",
+    "    wet days (RR >= R) is written to outfile. R is an optional parameter with default R = 1 mm.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
+    "",
+    "PARAMETER",
+    "    R  FLOAT   Precipitation threshold (unit: mm; default: R = 1 mm)",
+};
+
+const CdoHelp EcaSuHelp = {
+    "NAME",
+    "    eca_su, etccdi_su - Summer days index per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,T[,params]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily maximum temperature TX, then the number of days where ",
+    "    TX > T is counted. The number T is an optional parameter with default T = 25°C. ",
+    "    Note that TX have to be given in units of Kelvin, whereas T have to be given in degrees Celsius.",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_su     Summer days index per time period",
+    "               The operator counts over the entire time series.",
+    "               The date information of a timestep in outfile is the date of",
+    "               the last contributing timestep in infile.",
+    "    etccdi_su  Summer days index per time period",
+    "               The default output frequency is yearly.",
+    "               The date information of a timestep in outfile is the mid of",
+    "               the frequency interval.",
+    "",
+    "PARAMETER",
+    "    T     FLOAT     Temperature threshold (unit: °C; default: T = 25°C)",
+    "    freq  STRING    Output frequency (year, month)",
+};
+
+const CdoHelp EcaTg10pHelp = {
+    "NAME",
+    "    eca_tg10p - Cold days percent wrt 10th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_tg10p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily mean temperature TG, and",
+    "    infile2 be the 10th percentile TGn10 of daily mean temperatures",
+    "    for any period used as reference. Then the percentage of time where ",
+    "    TG < TGn10 is calculated.",
+    "    TGn10 is calculated as the 10th percentile of daily mean temperatures of a five ",
+    "    day window centred on each calendar day of a given climate reference period.",
+    "    Note that both TG and TGn10 have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaTg90pHelp = {
+    "NAME",
+    "    eca_tg90p - Warm days percent wrt 90th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_tg90p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily mean temperature TG, and",
+    "    infile2 be the 90th percentile TGn90 of daily mean temperatures",
+    "    for any period used as reference. Then the percentage of time where TG > TGn90 ",
+    "    is calculated. ",
+    "    TGn90 is calculated as the 90th percentile of daily mean temperatures of a five ",
+    "    day window centred on each calendar day of a given climate reference period.",
+    "    Note that both TG and TGn90 have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaTn10pHelp = {
+    "NAME",
+    "    eca_tn10p - Cold nights percent wrt 10th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_tn10p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time serie of the daily minimum temperature TN, and",
+    "    infile2 be the 10th percentile TNn10 of daily minimum temperatures",
+    "    for any period used as reference. Then the percentage of time where TN < TNn10 ",
+    "    is calculated.",
+    "    TNn10 is calculated as the 10th percentile of daily minimum temperatures of a five ",
+    "    day window centred on each calendar day of a given climate reference period.",
+    "    Note that both TN and TNn10 have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaTn90pHelp = {
+    "NAME",
+    "    eca_tn90p - Warm nights percent wrt 90th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_tn90p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily minimum temperature TN, and infile2 be the ",
+    "    90th percentile TNn90 of daily minimum temperatures for any period used as reference. ",
+    "    Then the percentage of time where TN > TNn90 is calculated. TNn90 is calculated as the 90th percentile",
+    "    of daily minimum temperatures of a five day window centred on each calendar day of a given climate",
+    "    reference period. Note that both TN and TNn90 have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaTrHelp = {
+    "NAME",
+    "    eca_tr, etccdi_tr - Tropical nights index per time period",
+    "",
+    "SYNOPSIS",
+    "    <operator>[,T[,params]]  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile be a time series of the daily minimum temperature TN, then the number of days where ",
+    "    TN > T is counted. The number T is an optional parameter with default T = 20°C. ",
+    "    Note that TN have to be given in units of Kelvin, whereas T have to be given in degrees Celsius.",
+    "    Parameter is a comma-separated list of \"key=values\" pairs.",
+    "",
+    "OPERATORS",
+    "    eca_tr     Tropical nights index per time period",
+    "               The operator counts over the entire time series.",
+    "               The date information of a timestep in outfile is the date of",
+    "               the last contributing timestep in infile.",
+    "    etccdi_tr  Tropical nights index per time period",
+    "               The default output frequency is yearly.",
+    "               The date information of a timestep in outfile is the mid of",
+    "               the frequency interval.",
+    "",
+    "PARAMETER",
+    "    T     FLOAT   Temperature threshold (unit: °C; default: T = 20°C)",
+    "    freq  STRING  Output frequency (year, month)",
+};
+
+const CdoHelp EcaTx10pHelp = {
+    "NAME",
+    "    eca_tx10p - Very cold days percent wrt 10th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_tx10p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily maximum temperature TX, and",
+    "    infile2 be the 10th percentile TXn10 of daily maximum temperatures",
+    "    for any period used as reference. Then the percentage of time where TX < TXn10.",
+    "    is calculated.",
+    "    TXn10 is calculated as the 10th percentile of daily maximum temperatures of a five ",
+    "    day window centred on each calendar day of a given climate reference period.",
+    "    Note that both TX and TXn10 have to be givenin the same units.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaTx90pHelp = {
+    "NAME",
+    "    eca_tx90p - Very warm days percent wrt 90th percentile of reference period",
+    "",
+    "SYNOPSIS",
+    "    eca_tx90p  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Let infile1 be a time series of the daily maximum temperature TX, and",
+    "    infile2 be the 90th percentile TXn90 of daily maximum temperatures",
+    "    for any period used as reference. Then the percentage of time where TX > TXn90.",
+    "    is calculated.",
+    "    TXn90 is calculated as the 90th percentile of daily maximum temperatures of a five ",
+    "    day window centred on each calendar day of a given climate reference period.",
+    "    Note that both TX and TXn90 have to be given in the same units.",
+    "    The date information of a timestep in outfile is the date of",
+    "    the last contributing timestep in infile1.",
+};
+
+const CdoHelp EcaEtccdiHelp = {
+    "NAME",
+    "    etccdi_tx90p, etccdi_tx10p, etccdi_tn90p, etccdi_tn10p, etccdi_r95p, ",
+    "    etccdi_r99p - ",
+    "    ETCCDI conform index for a reference periode calculated with bootstrapping",
+    "",
+    "SYNOPSIS",
+    "    <operator>,n,startboot,endboot[,m]  infile1 infile2 infile3 outfile",
+    "",
+    "DESCRIPTION",
+    "    This module enables to compute Climate Extremes Indices according to the method recommended",
+    "    by the Expert Team on Climate Change Detection and Indices. It differs from the",
+    "    corresponding eca_* indices by applying bootstrapping for a reference period",
+    "    (see Zhang et al. 2005) given by startboot and endboot and using the R-type 8 method ",
+    "    for percentile calculation.",
+    "    A requirement for correct percentile calculation is that",
+    "    CDO_PCTL_NBINS>=window*(endboot-startboot+1)*(sizeof(double)/sizeof(int))+2",
+    "    This demands for high working storage since the entire data of the bootstrapping interval",
+    "    need to be hold in storage. Otherwise, a histogram is used to calculate the percentile.",
+    "    infile2 (infile3) contains the daily minimum (maximum) of the bootstrapping interval.",
+    "    If m=m, the output variable will be saved monthly, otherwise with yearly frequency.",
+    "",
+    "OPERATORS",
+    "    etccdi_tx90p  Percentage of Days when Daily Maximum Temperature is Above the 90th Percentile",
+    "    etccdi_tx10p  Percentage of Days when Daily Maximum Temperature is Below the 10th Percentile",
+    "    etccdi_tn90p  Percentage of Days when Daily Minimum Temperature is Above the 90th Percentile",
+    "    etccdi_tn10p  Percentage of Days when Daily Minimum Temperature is Below the 10th Percentile",
+    "    etccdi_r95p   Annual Total Precipitation when Daily Precipitation Exceeds the 95th Percentile of Wet Day Precipitation",
+    "    etccdi_r99p   Annual Total Precipitation when Daily Precipitation Exceeds the 99th Percentile of Wet Day Precipitation",
+    "",
+    "PARAMETER",
+    "    n          INTEGER  Window days, number of timesteps",
+    "    startboot  INTEGER  First year of bootstrapping interval",
+    "    endboot    INTEGER  Last year of bootstrapping interval",
+    "    m          CHARACTER Output frequency",
+    "",
+    "ENVIRONMENT",
+    "    CDO_PCTL_NBINS",
+    "        Sets the number of histogram bins. The default number is 101.",
+};
+
+// clang-format on
+
diff --git a/src/operator_help.h b/src/operator_help.h
index 44c450995bdc3f30119f4cb34e44f7f98e7a79d7..1815b95ff6ea1601a2be16c0e123ab0251fc9e1f 100644
--- a/src/operator_help.h
+++ b/src/operator_help.h
@@ -3,7253 +3,227 @@
 #ifndef OPERATOR_HELP_H
 #define OPERATOR_HELP_H
 
-// clang-format off
-
-
-static const char *InfoHelp[] = {
-    "NAME",
-    "    info, infon, map - Information and simple statistics",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infiles",
-    "",
-    "DESCRIPTION",
-    "    This module writes information about the structure and contents for each field of all input files",
-    "    to standard output. A field is a horizontal layer of a data variable. All input files need to have ",
-    "    the same structure with the same variables on different timesteps.",
-    "    The information displayed depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    info   Dataset information listed by parameter identifier",
-    "           Prints information and simple statistics for each field of all input datasets.",
-    "           For each field the operator prints one line with the following elements:",
-    "           - Date and Time",
-    "           - Level, Gridsize and number of Missing values",
-    "           - Minimum, Mean and Maximum \\",
-    "           The mean value is computed without the use of area weights!",
-    "           - Parameter identifier",
-    "    infon  Dataset information listed by parameter name",
-    "           The same as operator info but using the name instead of the",
-    "           identifier to label the parameter.",
-    "    map    Dataset information and simple map",
-    "           Prints information, simple statistics and a map for each field of all input",
-    "           datasets. The map will be printed only for fields on a regular lon/lat grid.",
-    nullptr
-};
-
-static const char *SinfoHelp[] = {
-    "NAME",
-    "    sinfo, sinfon - Short information",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infiles",
-    "",
-    "DESCRIPTION",
-    "    This module writes information about the structure of infiles to standard output.",
-    "    infiles is an arbitrary number of input files. All input files need to have ",
-    "    the same structure with the same variables on different timesteps.",
-    "    The information displayed depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    sinfo   Short information listed by parameter identifier",
-    "            Prints short information of a dataset. The information is divided into 4 sections.",
-    "            Section 1 prints one line per parameter with the following information:",
-    "            - institute and source",
-    "            - time c=constant v=varying",
-    "            - type of statistical processing",
-    "            - number of levels and z-axis number",
-    "            - horizontal grid size and number",
-    "            - data type",
-    "            - parameter identifier",
-    "            Section 2 and 3 gives a short overview of all grid and vertical coordinates.",
-    "            And the last section contains short information of the time coordinate.",
-    "    sinfon  Short information listed by parameter name",
-    "            The same as operator sinfo but using the name instead of the identifier to label the parameter.",
-    nullptr
-};
-
-static const char *XSinfoHelp[] = {
-    "NAME",
-    "    xsinfo, xsinfop - Extra short information",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infiles",
-    "",
-    "DESCRIPTION",
-    "    This module writes information about the structure of infiles to standard output.",
-    "    infiles is an arbitrary number of input files. All input files need to have ",
-    "    the same structure with the same variables on different timesteps.",
-    "    The information displayed depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    xsinfo   Extra short information listed by parameter name",
-    "             Prints short information of a dataset. The information is divided into 4 sections.",
-    "             Section 1 prints one line per parameter with the following information:",
-    "             - institute and source",
-    "             - time c=constant v=varying",
-    "             - type of statistical processing",
-    "             - number of levels and z-axis number",
-    "             - horizontal grid size and number",
-    "             - data type",
-    "             - memory type (float or double)",
-    "             - parameter name",
-    "             Section 2 to 4 gives a short overview of all grid, vertical and time coordinates.",
-    "    xsinfop  Extra short information listed by parameter identifier",
-    "             The same as operator xsinfo but using the identifier instead of the name to label the parameter.",
-    nullptr
-};
-
-static const char *DiffHelp[] = {
-    "NAME",
-    "    diff, diffn - Compare two datasets field by field",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,options]  infile1 infile2",
-    "",
-    "DESCRIPTION",
-    "    Compares the contents of two datasets field by field. The input datasets need",
-    "    to have the same structure and its fields need to have the dimensions.",
-    "    Try the option names if the number of variables differ.",
-    "    Exit status is 0 if inputs are the same and 1 if they differ.",
-    "",
-    "OPERATORS",
-    "    diff   Compare two datasets listed by parameter id",
-    "           Provides statistics on differences between two datasets.",
-    "           For each pair of fields the operator prints one line with the following information:",
-    "           - Date and Time",
-    "           - Level, Gridsize and number of Missing values",
-    "           - Number of different values",
-    "           - Occurrence of coefficient pairs with different signs (S)",
-    "           - Occurrence of zero values (Z)",
-    "           - Maxima of absolute difference of coefficient pairs",
-    "           - Maxima of relative difference of non-zero coefficient pairs with equal signs",
-    "           - Parameter identifier",
-    "    diffn  Compare two datasets listed by parameter name",
-    "           The same as operator diff. Using the name instead of the",
-    "           identifier to label the parameter.",
-    "",
-    "PARAMETER",
-    "    maxcount  INTEGER Stop after maxcount different fields",
-    "    abslim    FLOAT   Limit of the maximum absolute difference (default: 0)",
-    "    rellim    FLOAT   Limit of the maximum relative difference (default: 1)",
-    "    names     STRING  Consideration of the variable names of only one input file (left/right) or the intersection of both (intersect).",
-    "              ",
-    nullptr
-};
-
-static const char *NinfoHelp[] = {
-    "NAME",
-    "    npar, nlevel, nyear, nmon, ndate, ntime, ngridpoints, ngrids - ",
-    "    Print the number of parameters, levels or times",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile",
-    "",
-    "DESCRIPTION",
-    "    This module prints the number of variables, levels or times of the ",
-    "    input dataset.",
-    "",
-    "OPERATORS",
-    "    npar         Number of parameters",
-    "                 Prints the number of parameters (variables).",
-    "    nlevel       Number of levels",
-    "                 Prints the number of levels for each variable.",
-    "    nyear        Number of years",
-    "                 Prints the number of different years.",
-    "    nmon         Number of months",
-    "                 Prints the number of different combinations of years and months.",
-    "    ndate        Number of dates",
-    "                 Prints the number of different dates.",
-    "    ntime        Number of timesteps",
-    "                 Prints the number of timesteps.",
-    "    ngridpoints  Number of gridpoints",
-    "                 Prints the number of gridpoints for each variable.",
-    "    ngrids       Number of horizontal grids",
-    "                 Prints the number of horizontal grids.",
-    nullptr
-};
-
-static const char *ShowinfoHelp[] = {
-    "NAME",
-    "    showformat, showcode, showname, showstdname, showlevel, showltype, showyear, ",
-    "    showmon, showdate, showtime, showtimestamp - Show variables, levels or times",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile",
-    "",
-    "DESCRIPTION",
-    "    This module prints the format, variables, levels or times of the input dataset.",
-    "",
-    "OPERATORS",
-    "    showformat     Show file format",
-    "                   Prints the file format of the input dataset.",
-    "    showcode       Show code numbers",
-    "                   Prints the code number of all variables.",
-    "    showname       Show variable names",
-    "                   Prints the name of all variables.",
-    "    showstdname    Show standard names",
-    "                   Prints the standard name of all variables.",
-    "    showlevel      Show levels",
-    "                   Prints all levels for each variable.",
-    "    showltype      Show GRIB level types",
-    "                   Prints the GRIB level type for all z-axes.",
-    "    showyear       Show years",
-    "                   Prints all years.",
-    "    showmon        Show months",
-    "                   Prints all months.",
-    "    showdate       Show date information",
-    "                   Prints date information of all timesteps (format YYYY-MM-DD).",
-    "    showtime       Show time information",
-    "                   Prints time information of all timesteps (format hh:mm:ss).",
-    "    showtimestamp  Show timestamp",
-    "                   Prints timestamp of all timesteps (format YYYY-MM-DDThh:mm:ss).",
-    nullptr
-};
-
-static const char *ShowattributeHelp[] = {
-    "NAME",
-    "    showattribute - Show attributes",
-    "",
-    "SYNOPSIS",
-    "    showattribute[,attributes]  infile",
-    "",
-    "DESCRIPTION",
-    "    This operator prints the attributes of the data variables of a dataset.",
-    "    ",
-    "    Each attribute has the following structure:",
-    "    ",
-    "      [var_nm@][att_nm]",
-    "    ",
-    "       var_nm  Variable name (optional). Example: pressure",
-    "       att_nm  Attribute name (optional). Example: units",
-    "    ",
-    "    The value of var_nm is the name of the variable containing the attribute (named att_nm) that",
-    "    you want to print. Use wildcards to print the attribute att_nm of more than one variable.",
-    "    A value of var_nm of '*' will print the attribute att_nm of all data variables.",
-    "    If var_nm is missing then att_nm refers to a global attribute.",
-    "    ",
-    "    The value of att_nm is the name of the attribute you want to print. Use wildcards to print more than",
-    "    one attribute. A value of att_nm of '*' will print all attributes.",
-    "",
-    "PARAMETER",
-    "    attributes  STRING  Comma-separated list of attributes. ",
-    nullptr
-};
-
-static const char *FiledesHelp[] = {
-    "NAME",
-    "    partab, codetab, griddes, zaxisdes, vct - Dataset description",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile",
-    "",
-    "DESCRIPTION",
-    "    This module provides operators to print meta information about a dataset.",
-    "    The printed meta-data depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    partab    Parameter table",
-    "              Prints all available meta information of the variables.",
-    "    codetab   Parameter code table",
-    "              Prints a code table with a description of all variables.",
-    "              For each variable the operator prints one line listing the",
-    "              code, name, description and units.",
-    "    griddes   Grid description",
-    "              Prints the description of all grids.",
-    "    zaxisdes  Z-axis description",
-    "              Prints the description of all z-axes.",
-    "    vct       Vertical coordinate table",
-    "              Prints the vertical coordinate table.",
-    nullptr
-};
-
-static const char *ApplyHelp[] = {
-    "NAME",
-    "    apply - Apply operators",
-    "",
-    "SYNOPSIS",
-    "    apply,operators  infiles",
-    "",
-    "DESCRIPTION",
-    "    The apply utility runs the named operators on each input file. The input files must be enclosed in square brackets.",
-    "    This utility can only be used on a series of input files. These are all operators with more than one input file (infiles).",
-    "    Here is an incomplete list of these operators: copy, cat, merge, mergetime, select, ENSSTAT.",
-    "    The parameter operators is a blank-separated list of CDO operators. Use quotation marks if more than one operator is needed.",
-    "    Each operator may have only one input and output stream.",
-    "",
-    "PARAMETER",
-    "    operators  STRING    Blank-separated list of CDO operators.",
-    nullptr
-};
-
-static const char *CopyHelp[] = {
-    "NAME",
-    "    copy, clone, cat - Copy datasets",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators to copy, clone or concatenate datasets.",
-    "    infiles is an arbitrary number of input files. All input files need to have ",
-    "    the same structure with the same variables on different timesteps.",
-    "",
-    "OPERATORS",
-    "    copy   Copy datasets",
-    "           Copies all input datasets to outfile. ",
-    "    clone  Clone datasets",
-    "           Copies all input datasets to outfile. In contrast to the copy operator, clone tries",
-    "           not to change the input data. GRIB records are neither decoded nor decompressed.",
-    "    cat    Concatenate datasets",
-    "           Concatenates all input datasets and appends the result to the end ",
-    "           of outfile. If outfile does not exist it will be created.",
-    nullptr
-};
-
-static const char *TeeHelp[] = {
-    "NAME",
-    "    tee - Duplicate a data stream and write it to file",
-    "",
-    "SYNOPSIS",
-    "    tee,outfile2  infile outfile1",
-    "",
-    "DESCRIPTION",
-    "    This operator copies the input dataset to outfile1 and outfile2. The first output stream",
-    "    in outfile1 can be further processesd with other cdo operators. The second output outfile2",
-    "    is written to disk. It can be used to store intermediate results to a file.",
-    "",
-    "PARAMETER",
-    "    outfile2  STRING Destination filename for the copy of the input file",
-    nullptr
-};
-
-static const char *PackHelp[] = {
-    "NAME",
-    "    pack - Pack data",
-    "",
-    "SYNOPSIS",
-    "    pack  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Packing reduces the data volume by reducing the precision of the stored numbers.",
-    "    It is implemented using the NetCDF attributes add_offset and scale_factor.",
-    "    The operator pack calculates the attributes add_offset and scale_factor for all variables.",
-    "    The default data type for all variables is automatically changed to 16-bit integer.",
-    "    Use the CDO option -b to change the data type to a different integer precision, if needed.",
-    "    Missing values are automatically transformed to the current data type.",
-    nullptr
-};
-
-static const char *UnpackHelp[] = {
-    "NAME",
-    "    unpack - Unpack data",
-    "",
-    "SYNOPSIS",
-    "    unpack  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Packing reduces the data volume by reducing the precision of the stored numbers.",
-    "    It is implemented using the NetCDF attributes add_offset and scale_factor.",
-    "    The operator unpack unpack all packed variables.",
-    "    The default data type for all variables is automatically changed to 32-bit floats.",
-    "    Use the CDO option -b F64 to change the data type to 64-bit floats, if needed.",
-    nullptr
-};
-
-static const char *BitroundingHelp[] = {
-    "NAME",
-    "    bitrounding - Bit rounding",
-    "",
-    "SYNOPSIS",
-    "    bitrounding[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator calculates for each field the number of necessary mantissa bits to get a certain",
-    "    information level in the data. With this number of significant bits (numbits) a rounding of the data is performed.",
-    "    This allows the data to be compressed to a higher level.",
-    "    ",
-    "    The default value of the information level is 0.9999 and can be adjusted with the parameter inflevel.",
-    "    That means 99.99% of the information in the mantissa bits is preserved.",
-    "    ",
-    "    Alternatively, the number of significant bits can be set for all variables with the numbits parameter.",
-    "    Furthermore, numbits can be assigned for each variable via the filename parameter. In this case, numbits is still",
-    "    calculated for all variables if they are not present in the file.",
-    "    ",
-    "    The analysis of the bit information is based on the Julia library BitInformation.jl (https://github.com/milankl/BitInformation.jl).",
-    "    The procedure to derive the number of significant mantissa bits was adapted from the Python library xbitinfo (https://github.com/observingClouds/xbitinfo).",
-    "    Quantize to the number of mantissa bits is done with IEEE rounding using code from NetCDF 4.9.0.",
-    "    ",
-    "    Currently only 32-bit float data is rounded. Data with missing values are not yet supported for the calculation of significant bits.",
-    "",
-    "PARAMETER",
-    "    inflevel   FLOAT   Information level (0 - 1) [default: 0.9999]",
-    "    addbits    INTEGER Add bits to the number of significant bits [default: 0]",
-    "    minbits    INTEGER Minimum value of the number of bits [default: 1]",
-    "    maxbits    INTEGER Maximum value of the number of bits [default: 23]",
-    "    numsteps   INTEGER Set to 1 to run the calculation only in the first time step",
-    "    numbits    INTEGER Set number of significant bits",
-    "    printbits  BOOL    Print max. numbits per variable of 1st timestep to stdout [format: name=numbits]",
-    "    filename   STRING  Read number of significant bits per variable from file [format: name=numbits]",
-    nullptr
-};
-
-static const char *ReplaceHelp[] = {
-    "NAME",
-    "    replace - Replace variables",
-    "",
-    "SYNOPSIS",
-    "    replace  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator replaces variables in infile1 by variables from infile2 and write",
-    "    the result to outfile. Both input datasets need to have the same number of timesteps.",
-    "    All variable names may only occur once!",
-    nullptr
-};
-
-static const char *DuplicateHelp[] = {
-    "NAME",
-    "    duplicate - Duplicates a dataset",
-    "",
-    "SYNOPSIS",
-    "    duplicate[,ndup]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator duplicates the contents of infile and writes the result to outfile.",
-    "    The optional parameter sets the number of duplicates, the default is 2.",
-    "",
-    "PARAMETER",
-    "    ndup  INTEGER  Number of duplicates, default is 2.",
-    nullptr
-};
-
-static const char *MergegridHelp[] = {
-    "NAME",
-    "    mergegrid - Merge grid",
-    "",
-    "SYNOPSIS",
-    "    mergegrid  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Merges grid points of all variables from infile2 to infile1 and write the result to outfile.",
-    "    Only the non missing values of infile2 will be used. The horizontal grid of infile2 should ",
-    "    be smaller or equal to the grid of infile1 and the resolution must be the same.",
-    "    Only rectilinear grids are supported. Both input files need to have the same variables ",
-    "    and the same number of timesteps.",
-    nullptr
-};
-
-static const char *MergeHelp[] = {
-    "NAME",
-    "    merge, mergetime - Merge datasets",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This module reads datasets from several input files, merges them and writes the resulting dataset to outfile.",
-    "",
-    "OPERATORS",
-    "    merge      Merge datasets with different fields",
-    "               Merges time series of different fields from several input datasets. The number ",
-    "               of fields per timestep written to outfile is the sum of the field numbers ",
-    "               per timestep in all input datasets. The time series on all input datasets are ",
-    "               required to have different fields and the same number of timesteps.",
-    "               The fields in each different input file either have to be different variables",
-    "               or different levels of the same variable. A mixture of different variables on",
-    "               different levels in different input files is not allowed.",
-    "    mergetime  Merge datasets sorted by date and time",
-    "               Merges all timesteps of all input files sorted by date and time.",
-    "               All input files need to have the same structure with the same variables on ",
-    "               different timesteps. After this operation every input timestep is in outfile ",
-    "               and all timesteps are sorted by date and time.",
-    "",
-    "ENVIRONMENT",
-    "    SKIP_SAME_TIME",
-    "        If set to 1, skips all consecutive timesteps with a double entry of the same timestamp.",
-    "",
-    "NOTE",
-    "    Operators of this module need to open all input files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *SplitHelp[] = {
-    "NAME",
-    "    splitcode, splitparam, splitname, splitlevel, splitgrid, splitzaxis, ",
-    "    splittabnum - Split a dataset",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,parameter]  infile obase",
-    "",
-    "DESCRIPTION",
-    "    This module splits infile into pieces. The output files will be named <obase><xxx><suffix>",
-    "    where suffix is the filename extension derived from the file format. xxx and the contents ",
-    "    of the output files depends on the chosen operator. ",
-    "    params is a comma-separated list of processing parameters.",
-    "",
-    "OPERATORS",
-    "    splitcode    Split code numbers",
-    "                 Splits a dataset into pieces, one for each different code number.",
-    "                 xxx will have three digits with the code number.",
-    "    splitparam   Split parameter identifiers",
-    "                 Splits a dataset into pieces, one for each different parameter identifier.",
-    "                 xxx will be a string with the parameter identifier.",
-    "    splitname    Split variable names",
-    "                 Splits a dataset into pieces, one for each variable name.",
-    "                 xxx will be a string with the variable name.",
-    "    splitlevel   Split levels",
-    "                 Splits a dataset into pieces, one for each different level.",
-    "                 xxx will have six digits with the level.",
-    "    splitgrid    Split grids",
-    "                 Splits a dataset into pieces, one for each different grid.",
-    "                 xxx will have two digits with the grid number.",
-    "    splitzaxis   Split z-axes",
-    "                 Splits a dataset into pieces, one for each different z-axis.",
-    "                 xxx will have two digits with the z-axis number.",
-    "    splittabnum  Split parameter table numbers",
-    "                 Splits a dataset into pieces, one for each GRIB1 parameter table number.",
-    "                 xxx will have three digits with the GRIB1 parameter table number.",
-    "",
-    "PARAMETER",
-    "    swap            STRING  Swap the position of obase and xxx in the output filename",
-    "    uuid=<attname>  STRING  Add a UUID as global attribute <attname> to each output file",
-    "",
-    "ENVIRONMENT",
-    "    CDO_FILE_SUFFIX",
-    "        Set the default file suffix. This suffix will be added to the output file ",
-    "        names instead of the filename extension derived from the file format. ",
-    "        Set this variable to NULL to disable the adding of a file suffix.",
-    "",
-    "NOTE",
-    "    Operators of this module need to open all output files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *SplittimeHelp[] = {
-    "NAME",
-    "    splithour, splitday, splitseas, splityear, splityearmon, splitmon - ",
-    "    Split timesteps of a dataset",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile obase",
-    "    splitmon[,format]  infile obase",
-    "",
-    "DESCRIPTION",
-    "    This module splits infile into  timesteps pieces. The output files will be named",
-    "    <obase><xxx><suffix> where suffix is the filename extension derived from the file format. ",
-    "    xxx and the contents of the output files depends on the chosen operator. ",
-    "",
-    "OPERATORS",
-    "    splithour     Split hours",
-    "                  Splits a file into pieces, one for each different hour.",
-    "                  xxx will have two digits with the hour.",
-    "    splitday      Split days",
-    "                  Splits a file into pieces, one for each different day.",
-    "                  xxx will have two digits with the day.",
-    "    splitseas     Split seasons",
-    "                  Splits a file into pieces, one for each different season.",
-    "                  xxx will have three characters with the season.",
-    "    splityear     Split years",
-    "                  Splits a file into pieces, one for each different year.",
-    "                  xxx will have four digits with the year (YYYY).",
-    "    splityearmon  Split in years and months",
-    "                  Splits a file into pieces, one for each different year and month.",
-    "                  xxx will have six digits with the year and month (YYYYMM).",
-    "    splitmon      Split months",
-    "                  Splits a file into pieces, one for each different month.",
-    "                  xxx will have two digits with the month.",
-    "",
-    "PARAMETER",
-    "    format  STRING  C-style format for strftime() (e.g. %B for the full month name)",
-    "",
-    "ENVIRONMENT",
-    "    CDO_FILE_SUFFIX",
-    "        Set the default file suffix. This suffix will be added to the output file ",
-    "        names instead of the filename extension derived from the file format. ",
-    "        Set this variable to NULL to disable the adding of a file suffix.",
-    "",
-    "NOTE",
-    "    Operators of this module need to open all output files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *SplitselHelp[] = {
-    "NAME",
-    "    splitsel - Split selected timesteps",
-    "",
-    "SYNOPSIS",
-    "    splitsel,nsets[,noffset[,nskip]]  infile obase",
-    "",
-    "DESCRIPTION",
-    "    This operator splits infile into pieces, one for each adjacent",
-    "    sequence t_1, ...., t_n of timesteps of the same selected time range.",
-    "    The output files will be named <obase><nnnnnn><suffix> where nnnnnn is the ",
-    "    sequence number and suffix is the filename extension derived from the file format.",
-    "",
-    "PARAMETER",
-    "    nsets    INTEGER  Number of input timesteps for each output file",
-    "    noffset  INTEGER  Number of input timesteps skipped before the first timestep range (optional)",
-    "    nskip    INTEGER  Number of input timesteps skipped between timestep ranges (optional)",
-    "",
-    "ENVIRONMENT",
-    "    CDO_FILE_SUFFIX",
-    "        Set the default file suffix. This suffix will be added to the output file ",
-    "        names instead of the filename extension derived from the file format. ",
-    "        Set this variable to NULL to disable the adding of a file suffix.",
-    nullptr
-};
-
-static const char *SplitdateHelp[] = {
-    "NAME",
-    "    splitdate - Splits a file into dates",
-    "",
-    "SYNOPSIS",
-    "    splitdate  infile obase",
-    "",
-    "DESCRIPTION",
-    "    This operator splits infile into pieces, one for each different date.",
-    "    The output files will be named <obase><YYYY-MM-DD><suffix> where YYYY-MM-DD is the ",
-    "    date and suffix is the filename extension derived from the file format.",
-    "",
-    "ENVIRONMENT",
-    "    CDO_FILE_SUFFIX",
-    "        Set the default file suffix. This suffix will be added to the output file ",
-    "        names instead of the filename extension derived from the file format. ",
-    "        Set this variable to NULL to disable the adding of a file suffix.",
-    nullptr
-};
-
-static const char *DistgridHelp[] = {
-    "NAME",
-    "    distgrid - Distribute horizontal grid",
-    "",
-    "SYNOPSIS",
-    "    distgrid,nx[,ny]  infile obase",
-    "",
-    "DESCRIPTION",
-    "    This operator distributes a dataset into smaller pieces. Each output file contains a different region of the",
-    "    horizontal source grid. 2D Lon/Lat grids can be split into nx*ny pieces, where a target grid region contains",
-    "    a structured longitude/latitude box of the source grid. Data on an unstructured grid is split into nx pieces.",
-    "    The output files will be named <obase><xxx><suffix> where suffix is the filename extension derived from the",
-    "    file format. xxx will have five digits with the number of the target region.",
-    "",
-    "PARAMETER",
-    "    nx  INTEGER  Number of regions in x direction, or number of pieces for unstructured grids",
-    "    ny  INTEGER  Number of regions in y direction [default: 1]",
-    "",
-    "NOTE",
-    "    This operator needs to open all output files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *CollgridHelp[] = {
-    "NAME",
-    "    collgrid - Collect horizontal grid",
-    "",
-    "SYNOPSIS",
-    "    collgrid[,nx[,names]]  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator collects the data of the input files to one output file. All input files need",
-    "    to have the same variables and the same number of timesteps on a different horizonal grid region.",
-    "    If the source regions are on a structured lon/lat grid, all regions together must result in a",
-    "    new structured lat/long grid box. Data on an unstructured grid is concatenated in the order of",
-    "    the input files. The parameter nx needs to be specified only for curvilinear grids.",
-    "",
-    "PARAMETER",
-    "    nx     INTEGER  Number of regions in x direction [default: number of input files]",
-    "    names  STRING   Comma-separated list of variable names [default: all variables]",
-    "",
-    "NOTE",
-    "    This operator needs to open all input files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *SelectHelp[] = {
-    "NAME",
-    "    select, delete - Select fields",
-    "",
-    "SYNOPSIS",
-    "    <operator>,parameter  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This module selects some fields from infiles and writes them to outfile.",
-    "    infiles is an arbitrary number of input files. All input files need to have ",
-    "    the same structure with the same variables on different timesteps.",
-    "    The fields selected depends on the chosen parameters. Parameter is a comma-separated list",
-    "    of \"key=value\" pairs. A range of integer values can be specified by first/last[/inc].",
-    "    Wildcards are supported for string values.",
-    "",
-    "OPERATORS",
-    "    select  Select fields",
-    "            Selects all fields with parameters in a user given list.",
-    "    delete  Delete fields",
-    "            Deletes all fields with parameters in a user given list.",
-    "",
-    "PARAMETER",
-    "    name              STRING  Comma-separated list of variable names.",
-    "    param             STRING  Comma-separated list of parameter identifiers.",
-    "    code              INTEGER Comma-separated list or first/last[/inc] range of code numbers.",
-    "    level             FLOAT   Comma-separated list of vertical levels.",
-    "    levrange          FLOAT   First and last value of the level range.",
-    "    levidx            INTEGER Comma-separated list or first/last[/inc] range of index of levels.",
-    "    zaxisname         STRING  Comma-separated list of zaxis names.",
-    "    zaxisnum          INTEGER Comma-separated list or first/last[/inc] range of zaxis numbers.",
-    "    ltype             INTEGER Comma-separated list or first/last[/inc] range of GRIB level types.",
-    "    gridname          STRING  Comma-separated list of grid names.",
-    "    gridnum           INTEGER Comma-separated list or first/last[/inc] range of grid numbers.",
-    "    steptype          STRING  Comma-separated list of timestep types (constant, avg, accum, min, max, range, diff, sum)",
-    "    date              STRING  Comma-separated list of dates (format YYYY-MM-DDThh:mm:ss).",
-    "    startdate         STRING  Start date (format YYYY-MM-DDThh:mm:ss).",
-    "    enddate           STRING  End date (format YYYY-MM-DDThh:mm:ss).",
-    "    minute            INTEGER Comma-separated list or first/last[/inc] range of minutes.",
-    "    hour              INTEGER Comma-separated list or first/last[/inc] range of hours.",
-    "    day               INTEGER Comma-separated list or first/last[/inc] range of days.",
-    "    month             INTEGER Comma-separated list or first/last[/inc] range of months.",
-    "    season            STRING  Comma-separated list of seasons (substring of DJFMAMJJASOND or ANN).",
-    "    year              INTEGER Comma-separated list or first/last[/inc] range of years.",
-    "    dom               STRING  Comma-separated list of the day of month (e.g. 29feb).",
-    "    timestep          INTEGER Comma-separated list or first/last[/inc] range of timesteps. Negative values select timesteps from the end (NetCDF only).",
-    "    timestep_of_year  INTEGER Comma-separated list or first/last[/inc] range of timesteps of year.",
-    "    timestepmask      STRING  Read timesteps from a mask file.",
-    nullptr
-};
-
-static const char *SelmultiHelp[] = {
-    "NAME",
-    "    selmulti, delmulti, changemulti - Select multiple fields via GRIB1 parameters",
-    "",
-    "SYNOPSIS",
-    "    <operator>,selection-specification  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module selects multiple fields from infile and writes them to outfile.",
-    "    selection-specification is a filename or in-place string with the selection specification.",
-    "    Each selection-specification has the following compact notation format: ",
-    "    ",
-    "       <type>(parameters; leveltype(s); levels)",
-    "    ",
-    "    type      "    "    sel for select or del for delete (optional)",
-    "    parameters"    "    GRIB1 parameter code number",
-    "    leveltype "    "    GRIB1 level type",
-    "    levels    "    "    value of each level",
-    "    ",
-    "    Examples:",
-    "    ",
-    "       (1; 103; 0) ",
-    "       (33,34; 105; 10) ",
-    "       (11,17; 105; 2) ",
-    "       (71,73,74,75,61,62,65,117,67,122,121,11,131,66,84,111,112; 105; 0) ",
-    "    ",
-    "    The following descriptive notation can also be used for selection specification from a file:",
-    "    ",
-    "       SELECT/DELETE, PARAMETER=parameters, LEVTYPE=leveltye(s), LEVEL=levels",
-    "    ",
-    "    Examples:",
-    "    ",
-    "       SELECT, PARAMETER=1, LEVTYPE=103, LEVEL=0 ",
-    "       SELECT, PARAMETER=33/34, LEVTYPE=105, LEVEL=10 ",
-    "       SELECT, PARAMETER=11/17, LEVTYPE=105, LEVEL=2 ",
-    "       SELECT, PARAMETER=71/73/74/75/61/62/65/117/67/122, LEVTYPE=105, LEVEL=0 ",
-    "       DELETE, PARAMETER=128, LEVTYPE=109, LEVEL=* ",
-    "    ",
-    "    The following will convert Pressure from Pa into hPa; Temp from Kelvin to Celsius: ",
-    "       SELECT, PARAMETER=1, LEVTYPE= 103, LEVEL=0, SCALE=0.01 ",
-    "       SELECT, PARAMETER=11, LEVTYPE=105, LEVEL=2, OFFSET=273.15 ",
-    "    If SCALE and/or OFFSET are defined, then the data values are scaled as SCALE*(VALUE-OFFSET).",
-    "",
-    "OPERATORS",
-    "    selmulti     Select multiple fields",
-    "    delmulti     Delete multiple fields",
-    "    changemulti  Change identication of multiple fields",
-    nullptr
-};
-
-static const char *SelvarHelp[] = {
-    "NAME",
-    "    selparam, delparam, selcode, delcode, selname, delname, selstdname, sellevel, ",
-    "    sellevidx, selgrid, selzaxis, selzaxisname, selltype, seltabnum - Select fields",
-    "",
-    "SYNOPSIS",
-    "    <operator>,parameter  infile outfile",
-    "    selcode,codes  infile outfile",
-    "    delcode,codes  infile outfile",
-    "    selname,names  infile outfile",
-    "    delname,names  infile outfile",
-    "    selstdname,stdnames  infile outfile",
-    "    sellevel,levels  infile outfile",
-    "    sellevidx,levidx  infile outfile",
-    "    selgrid,grids  infile outfile",
-    "    selzaxis,zaxes  infile outfile",
-    "    selzaxisname,zaxisnames  infile outfile",
-    "    selltype,ltypes  infile outfile",
-    "    seltabnum,tabnums  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module selects some fields from infile and writes them to outfile.",
-    "    The fields selected depends on the chosen operator and the parameters. A range of integer",
-    "    values can be specified by first/last[/inc].",
-    "",
-    "OPERATORS",
-    "    selparam      Select parameters by identifier",
-    "                  Selects all fields with parameter identifiers in a user given list.",
-    "    delparam      Delete parameters by identifier",
-    "                  Deletes all fields with parameter identifiers in a user given list.",
-    "    selcode       Select parameters by code number",
-    "                  Selects all fields with code numbers in a user given list or range.",
-    "    delcode       Delete parameters by code number",
-    "                  Deletes all fields with code numbers in a user given list or range.",
-    "    selname       Select parameters by name",
-    "                  Selects all fields with parameter names in a user given list.",
-    "    delname       Delete parameters by name",
-    "                  Deletes all fields with parameter names in a user given list.",
-    "    selstdname    Select parameters by standard name",
-    "                  Selects all fields with standard names in a user given list.",
-    "    sellevel      Select levels",
-    "                  Selects all fields with levels in a user given list.",
-    "    sellevidx     Select levels by index",
-    "                  Selects all fields with index of levels in a user given list or range.",
-    "    selgrid       Select grids",
-    "                  Selects all fields with grids in a user given list.",
-    "    selzaxis      Select z-axes",
-    "                  Selects all fields with z-axes in a user given list.",
-    "    selzaxisname  Select z-axes by name",
-    "                  Selects all fields with z-axis names in a user given list.",
-    "    selltype      Select GRIB level types",
-    "                  Selects all fields with GRIB level type in a user given list or range.",
-    "    seltabnum     Select parameter table numbers",
-    "                  Selects all fields with parameter table numbers in a user given list or range.",
-    "",
-    "PARAMETER",
-    "    parameter   STRING   Comma-separated list of parameter identifiers.",
-    "    codes       INTEGER  Comma-separated list or first/last[/inc] range of code numbers.",
-    "    names       STRING   Comma-separated list of variable names.",
-    "    stdnames    STRING   Comma-separated list of standard names.",
-    "    levels      FLOAT    Comma-separated list of vertical levels.",
-    "    levidx      INTEGER  Comma-separated list or first/last[/inc] range of index of levels.",
-    "    ltypes      INTEGER  Comma-separated list or first/last[/inc] range of GRIB level types.",
-    "    grids       STRING   Comma-separated list of grid names or numbers.",
-    "    zaxes       STRING   Comma-separated list of z-axis types or numbers.",
-    "    zaxisnames  STRING   Comma-separated list of z-axis names.",
-    "    tabnums     INTEGER  Comma-separated list or range of parameter table numbers.",
-    nullptr
-};
-
-static const char *SeltimeHelp[] = {
-    "NAME",
-    "    seltimestep, seltime, selhour, selday, selmonth, selyear, selseason, seldate, ",
-    "    selsmon - Select timesteps",
-    "",
-    "SYNOPSIS",
-    "    seltimestep,timesteps  infile outfile",
-    "    seltime,times  infile outfile",
-    "    selhour,hours  infile outfile",
-    "    selday,days  infile outfile",
-    "    selmonth,months  infile outfile",
-    "    selyear,years  infile outfile",
-    "    selseason,seasons  infile outfile",
-    "    seldate,startdate[,enddate]  infile outfile",
-    "    selsmon,month[,nts1[,nts2]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module selects user specified timesteps from infile and writes them to outfile.",
-    "    The timesteps selected depends on the chosen operator and the parameters. A range of integer values",
-    "    can be specified by first/last[/inc].",
-    "",
-    "OPERATORS",
-    "    seltimestep  Select timesteps",
-    "                 Selects all timesteps with a timestep in a user given list or range.",
-    "    seltime      Select times",
-    "                 Selects all timesteps with a time in a user given list or range.",
-    "    selhour      Select hours",
-    "                 Selects all timesteps with a hour in a user given list or range.",
-    "    selday       Select days",
-    "                 Selects all timesteps with a day in a user given list or range.",
-    "    selmonth     Select months",
-    "                 Selects all timesteps with a month in a user given list or range.",
-    "    selyear      Select years",
-    "                 Selects all timesteps with a year in a user given list or range.",
-    "    selseason    Select seasons",
-    "                 Selects all timesteps with a month of a season in a user given list.",
-    "    seldate      Select dates",
-    "                 Selects all timesteps with a date in a user given range.",
-    "    selsmon      Select single month",
-    "                 Selects a month and optional an arbitrary number of timesteps before and after this month.",
-    "",
-    "PARAMETER",
-    "    timesteps  INTEGER  Comma-separated list or first/last[/inc] range of timesteps. Negative values select timesteps from the end (NetCDF only).",
-    "    times      STRING   Comma-separated list of times (format hh:mm:ss).",
-    "    hours      INTEGER  Comma-separated list or first/last[/inc] range of hours.",
-    "    days       INTEGER  Comma-separated list or first/last[/inc] range of days.",
-    "    months     INTEGER  Comma-separated list or first/last[/inc] range of months.",
-    "    years      INTEGER  Comma-separated list or first/last[/inc] range of years.",
-    "    seasons    STRING   Comma-separated list of seasons (substring of DJFMAMJJASOND or ANN).",
-    "    startdate  STRING   Start date (format YYYY-MM-DDThh:mm:ss).",
-    "    enddate    STRING   End date (format YYYY-MM-DDThh:mm:ss) [default: startdate].",
-    "    nts1       INTEGER  Number of timesteps before the selected month [default: 0].",
-    "    nts2       INTEGER  Number of timesteps after the selected month [default: nts1].",
-    nullptr
-};
-
-static const char *SelboxHelp[] = {
-    "NAME",
-    "    sellonlatbox, selindexbox - Select a box",
-    "",
-    "SYNOPSIS",
-    "    sellonlatbox,lon1,lon2,lat1,lat2  infile outfile",
-    "    selindexbox,idx1,idx2,idy1,idy2  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Selects grid cells inside a lon/lat or index box.",
-    "",
-    "OPERATORS",
-    "    sellonlatbox  Select a longitude/latitude box",
-    "                  Selects grid cells inside a lon/lat box. The user must specify the longitude and latitude of the edges of the box.",
-    "                  Only those grid cells are considered whose grid center lies within the lon/lat box.",
-    "                  For rotated lon/lat grids the parameters must be specified in rotated coordinates.",
-    "    selindexbox   Select an index box",
-    "                  Selects grid cells within an index box. The user must specify the indices of the edges of the box.",
-    "                  The index of the left edge can be greater then the one of the right edge. Use negative indexing to",
-    "                  start from the end. The input grid must be a regular lon/lat or a 2D curvilinear grid.",
-    "",
-    "PARAMETER",
-    "    lon1  FLOAT    Western longitude in degrees",
-    "    lon2  FLOAT    Eastern longitude in degrees",
-    "    lat1  FLOAT    Southern or northern latitude in degrees",
-    "    lat2  FLOAT    Northern or southern latitude in degrees",
-    "    idx1  INTEGER  Index of first longitude (1 - nlon)",
-    "    idx2  INTEGER  Index of last longitude (1 - nlon)",
-    "    idy1  INTEGER  Index of first latitude (1 - nlat)",
-    "    idy2  INTEGER  Index of last latitude (1 - nlat)",
-    nullptr
-};
-
-static const char *SelregionHelp[] = {
-    "NAME",
-    "    selregion, selcircle - Select horizontal regions",
-    "",
-    "SYNOPSIS",
-    "    selregion,regions  infile outfile",
-    "    selcircle[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Selects all grid cells with the center point inside user defined regions or a circle.",
-    "    The resulting grid is unstructured.",
-    "",
-    "OPERATORS",
-    "    selregion  Select cells inside regions",
-    "               Selects all grid cells with the center point inside the regions.",
-    "               Regions can be defined by the user via an ASCII file.",
-    "               Each region consists of the geographic coordinates of a convex polygon.",
-    "               Each line of a polygon description file contains the longitude and latitude of one point.",
-    "               Each polygon description file can contain one or more polygons separated by a line with the character \\&.",
-    "               ",
-    "               Predefined regions of countries can be specified via the country codes.",
-    "               A country is specified with dcw:<CountryCode>. Country codes can be combined with the plus sign.",
-    "    selcircle  Select cells inside a circle",
-    "               Selects all grid cells with the center point inside a circle. The circle is described by geographic coordinates",
-    "               of the center and the radius of the circle.",
-    "",
-    "PARAMETER",
-    "    regions  STRING   Comma-separated list of ASCII formatted files with different regions",
-    "    lon      FLOAT    Longitude of the center of the circle in degrees, default lon=0.0",
-    "    lat      FLOAT    Latitude of the center of the circle in degrees, default lat=0.0",
-    "    radius   STRING   Radius of the circle, default radius=1deg (units: deg, rad, km, m)",
-    nullptr
-};
-
-static const char *SelgridcellHelp[] = {
-    "NAME",
-    "    selgridcell, delgridcell - Select grid cells",
-    "",
-    "SYNOPSIS",
-    "    <operator>,indices  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    The operator selects grid cells of all fields from infile. The user must specify the index of each grid cell.",
-    "    The resulting grid in outfile is unstructured.",
-    "",
-    "OPERATORS",
-    "    selgridcell  Select grid cells",
-    "    delgridcell  Delete grid cells",
-    "",
-    "PARAMETER",
-    "    indices  INTEGER  Comma-separated list or first/last[/inc] range of indices",
-    nullptr
-};
-
-static const char *SamplegridHelp[] = {
-    "NAME",
-    "    samplegrid - Resample grid",
-    "",
-    "SYNOPSIS",
-    "    samplegrid,factor  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This is a special operator for resampling the horizontal grid.",
-    "    No interpolation takes place. Resample factor=2 means every second grid point is removed.",
-    "    Only rectilinear and curvilinear source grids are supported by this operator.",
-    "",
-    "PARAMETER",
-    "    factor  INTEGER  Resample factor, typically 2, which will half the resolution",
-    nullptr
-};
-
-static const char *SelyearidxHelp[] = {
-    "NAME",
-    "    selyearidx - Select year by index",
-    "",
-    "SYNOPSIS",
-    "    selyearidx  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Selects field elements from infile2 by a yearly time index from infile1.",
-    "    The yearly indices in infile1 should be the result of corresponding yearminidx and yearmaxidx operations, respectively.",
-    nullptr
-};
-
-static const char *SelsurfaceHelp[] = {
-    "NAME",
-    "    bottomvalue, topvalue, isosurface - Extract surface",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "    isosurface,isovalue  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes a surface from all 3D variables. The result is a horizonal 2D field.",
-    "",
-    "OPERATORS",
-    "    bottomvalue  Extract bottom level",
-    "                 This operator selects the valid values at the bottom level.",
-    "                 The NetCDF CF compliant attribute positive is used to determine where top and bottom are.",
-    "                 If this attribute is missing, low values are bottom and high values are top.",
-    "    topvalue     Extract top level",
-    "                 This operator selects the valid values at the top level.",
-    "                 The NetCDF CF compliant attribute positive is used to determine where top and bottom are.",
-    "                 If this attribute is missing, low values are bottom and high values are top.",
-    "    isosurface   Extract isosurface",
-    "                 This operator computes an isosurface. The value of the isosurfce is specified by the parameter isovalue.",
-    "                 The isosurface is calculated by linear interpolation between two layers.",
-    "",
-    "PARAMETER",
-    "    isovalue  FLOAT  Isosurface value",
-    nullptr
-};
-
-static const char *CondHelp[] = {
-    "NAME",
-    "    ifthen, ifnotthen - Conditional select one field",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module selects field elements from infile2 with respect to infile1 and writes them ",
-    "    to outfile. The fields in infile1 are handled as a mask. A value ",
-    "    not equal to zero is treated as \"true\", zero is treated as \"false\".",
-    "    The number of fields in infile1 has either to be the same as in infile2 or the",
-    "    same as in one timestep of infile2 or only one.",
-    "    The fields in outfile inherit the meta data from infile2.",
-    "",
-    "OPERATORS",
-    "    ifthen     If then",
-    "                        / i_2(t,x) if i_1(t,x) NE 0  AND  i_1(t,x) NE miss",
-    "               o(t,x) =",
-    "                        \\ miss     if i_1(t,x) EQ 0  OR   i_1(t,x) EQ miss",
-    "    ifnotthen  If not then",
-    "                        / i_2(t,x) if i_1(t,x) EQ 0  AND  i_1(t,x) NE miss",
-    "               o(t,x) = ",
-    "                        \\ miss     if i_1(t,x) NE 0  OR   i_1(t,x) EQ miss",
-    nullptr
-};
-
-static const char *Cond2Help[] = {
-    "NAME",
-    "    ifthenelse - Conditional select  two fields",
-    "",
-    "SYNOPSIS",
-    "    ifthenelse  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator selects field elements from infile2 or infile3 with respect to",
-    "    infile1 and writes them to outfile. The fields in infile1 are handled as a mask.",
-    "    A value not equal to zero is treated as \"true\", zero is treated as \"false\".",
-    "    The number of fields in infile1 has either to be the same as in infile2 or the ",
-    "    same as in one timestep of infile2 or only one.",
-    "    infile2 and infile3 need to have the same number of fields.",
-    "    The fields in outfile inherit the meta data from infile2.",
-    "    ",
-    "              / i_2(t,x) if i_1(t,x) NE 0  AND  i_1(t,x) NE miss",
-    "    o(t,x) = <  i_3(t,x) if i_1(t,x) EQ 0  AND  i_1[t,x) NE miss",
-    "              \\ miss     if i_1(t,x) EQ miss",
-    nullptr
-};
-
-static const char *CondcHelp[] = {
-    "NAME",
-    "    ifthenc, ifnotthenc - Conditional select a constant",
-    "",
-    "SYNOPSIS",
-    "    <operator>,c  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module creates fields with a constant value or missing value.",
-    "    The fields in infile are handled as a mask. A value not equal ",
-    "    to zero is treated as \"true\", zero is treated as \"false\".",
-    "",
-    "OPERATORS",
-    "    ifthenc     If then constant",
-    "                         / c      if i(t,x) NE 0  AND  i(t,x) NE miss",
-    "                o(t,x) =",
-    "                         \\ miss   if i(t,x) EQ 0  OR   i(t,x) EQ miss",
-    "    ifnotthenc  If not then constant",
-    "                         / c      if i(t,x) EQ 0  AND  i(t,x) NE miss",
-    "                o(t,x) =",
-    "                         \\ miss   if i(t,x) NE 0  OR   i(t,x) EQ miss",
-    "",
-    "PARAMETER",
-    "    c  FLOAT  Constant",
-    nullptr
-};
-
-static const char *MapReduceHelp[] = {
-    "NAME",
-    "    reducegrid - Reduce fields to user-defined mask",
-    "",
-    "SYNOPSIS",
-    "    reducegrid,mask[,limitCoordsOutput]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module holds an operator for data reduction based on a user defined mask.",
-    "    The output grid is unstructured and includes coordinate bounds. Bounds can be",
-    "    avoided by using the additional 'nobounds' keyword. With 'nocoords' given,",
-    "    coordinates a completely suppressed.",
-    "",
-    "PARAMETER",
-    "    mask               STRING file which holds the mask field",
-    "    limitCoordsOutput  STRING optional parameter to limit coordinates output: 'nobounds' disables coordinate bounds, 'nocoords' avoids all coordinate information",
-    nullptr
-};
-
-static const char *CompHelp[] = {
-    "NAME",
-    "    eq, ne, le, lt, ge, gt - Comparison of two fields",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module compares two datasets field by field.",
-    "    The resulting field is a mask containing 1 if the comparison is true and 0 if not.",
-    "    The number of fields in infile1 should be the same as in infile2.",
-    "    One of the input files can contain only one timestep or one field.",
-    "    The fields in outfile inherit the meta data from infile1 or infile2.",
-    "    The type of comparison depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    eq  Equal",
-    "                  /   1   if i_1(t,x) EQ i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "        o(t,x) = <    0   if i_1(t,x) NE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
-    "    ne  Not equal",
-    "                  /   1   if i_1(t,x) NE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "        o(t,x) = <    0   if i_1(t,x) EQ i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
-    "    le  Less equal",
-    "                  /   1   if i_1(t,x) LE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "        o(t,x) = <    0   if i_1(t,x) GT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
-    "    lt  Less than",
-    "                  /   1   if i_1(t,x) LT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "        o(t,x) = <    0   if i_1(t,x) GE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
-    "    ge  Greater equal",
-    "                  /   1   if i_1(t,x) GE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "        o(t,x) = <    0   if i_1(t,x) LT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
-    "    gt  Greater than",
-    "                  /   1   if i_1(t,x) GT i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "        o(t,x) = <    0   if i_1(t,x) LE i_2(t,x)  AND  i_1(t,x),i_2(t,x) NE miss",
-    "                  \\  miss if i_1(t,x) EQ miss      OR   i_2(t,x) EQ miss",
-    nullptr
-};
-
-static const char *CompcHelp[] = {
-    "NAME",
-    "    eqc, nec, lec, ltc, gec, gtc - Comparison of a field with a constant",
-    "",
-    "SYNOPSIS",
-    "    <operator>,c  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module compares all fields of a dataset with a constant. The resulting",
-    "    field is a mask containing 1 if the comparison is true and 0 if not.",
-    "    The type of comparison depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    eqc  Equal constant",
-    "                   /   1   if i(t,x) EQ c     AND  i(t,x),c NE miss",
-    "         o(t,x) = <    0   if i(t,x) NE c     AND  i(t,x),c NE miss",
-    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
-    "    nec  Not equal constant",
-    "                   /   1   if i(t,x) NE c     AND  i(t,x),c NE miss",
-    "         o(t,x) = <    0   if i(t,x) EQ c     AND  i(t,x),c NE miss",
-    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
-    "    lec  Less equal constant",
-    "                   /   1   if i(t,x) LE c     AND  i(t,x),c NE miss",
-    "         o(t,x) = <    0   if i(t,x) GT c     AND  i(t,x),c NE miss",
-    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
-    "    ltc  Less than constant",
-    "                   /   1   if i(t,x) LT c     AND  i(t,x),c NE miss",
-    "         o(t,x) = <    0   if i(t,x) GE c     AND  i(t,x),c NE miss",
-    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
-    "    gec  Greater equal constant",
-    "                   /   1   if i(t,x) GE c     AND  i(t,x),c NE miss",
-    "         o(t,x) = <    0   if i(t,x) LT c     AND  i(t,x),c NE miss",
-    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
-    "    gtc  Greater than constant",
-    "                   /   1   if i(t,x) GT c     AND  i(t,x),c NE miss",
-    "         o(t,x) = <    0   if i(t,x) LE c     AND  i(t,x),c NE miss",
-    "                   \\  miss if i(t,x) EQ miss  OR   c EQ miss",
-    "",
-    "PARAMETER",
-    "    c  FLOAT  Constant",
-    nullptr
-};
-
-static const char *YmoncompHelp[] = {
-    "NAME",
-    "    ymoneq, ymonne, ymonle, ymonlt, ymonge, ymongt - Multi-year monthly comparison",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs compaisons of a time series and one timestep with the same month of year.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same month of year is used.",
-    "    The resulting field is a mask containing 1 if the comparison is true and 0 if not. ",
-    "    The type of comparison depends on the chosen operator.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module YMONSTAT.",
-    "",
-    "OPERATORS",
-    "    ymoneq  Compare time series with Equal",
-    "            Compares whether a time series is equal to a multi-year monthly time series.",
-    "    ymonne  Compare time series with NotEqual",
-    "            Compares whether a time series is not equal to a multi-year monthly time series.",
-    "    ymonle  Compare time series with LessEqual",
-    "            Compares whether a time series is less than or equal to a multi-year monthly time series.",
-    "    ymonlt  Compares if time series with LessThan",
-    "            Compares whether a time series is less than a multi-year monthly time series.",
-    "    ymonge  Compares if time series with GreaterEqual",
-    "            Compares whether a time series is greater than or equal to a multi-year monthly time series.",
-    "    ymongt  Compares if time series with GreaterThan",
-    "            Compares whether a time series is greater than a multi-year monthly time series.",
-    nullptr
-};
-
-static const char *SetattributeHelp[] = {
-    "NAME",
-    "    setattribute - Set attributes",
-    "",
-    "SYNOPSIS",
-    "    setattribute,attributes  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator sets attributes of a dataset and writes the result to outfile.",
-    "    The new attributes are only available in outfile if the file format supports attributes.",
-    "    ",
-    "    Each attribute has the following structure:",
-    "    ",
-    "      [var_nm@]att_nm[:{s|d|i}]=[att_val|{[var_nm@]att_nm}]",
-    "    ",
-    "       var_nm  Variable name (optional). Example: pressure",
-    "       att_nm  Attribute name. Example: units",
-    "       att_val Comma-separated list of attribute values. Example: pascal",
-    "    ",
-    "    The value of var_nm is the name of the variable containing the attribute (named att_nm) that",
-    "    you want to set. Use wildcards to set the attribute att_nm to more than one variable.",
-    "    A value of var_nm of '*' will set the attribute att_nm to all data variables.",
-    "    If var_nm is missing then att_nm refers to a global attribute.",
-    "    ",
-    "    The value of att_nm is the name of the attribute you want to set. For each attribute a string (att_nm:s),",
-    "    a double (att_nm:d) or an integer (att_nm:i) type can be defined. By default the native type is set.",
-    "    ",
-    "    The value of att_val is the contents of the attribute att_nm. att_val may be a single value",
-    "    or one-dimensional array of elements. The type and the number of elements of an attribute will be detected",
-    "    automatically from the contents of the values. An already existing attribute att_nm will be overwritten",
-    "    or it will be removed if att_val is omitted. Alternatively, the values of an existing attribute can be copied.",
-    "    This attribute must then be enclosed in curly brackets.",
-    "    ",
-    "    A special meaning has the attribute name FILE. If this is the 1st attribute then all attributes",
-    "    are read from a file specified in the value of att_val.",
-    "",
-    "PARAMETER",
-    "    attributes  STRING  Comma-separated list of attributes. ",
-    "",
-    "NOTE",
-    "    Attributes are evaluated by CDO when opening infile. Therefor the result of this operator is not available",
-    "    for other operators when this operator is used in chaining operators.",
-    nullptr
-};
-
-static const char *SetpartabHelp[] = {
-    "NAME",
-    "    setpartabp, setpartabn - Set parameter table",
-    "",
-    "SYNOPSIS",
-    "    <operator>,table[,convert]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module transforms data and metadata of infile via a parameter table and writes the result to outfile.",
-    "    A parameter table is an ASCII formatted file with a set of parameter entries for each variable. Each new set have to",
-    "    start with \"\\&parameter\" and to end with \"/\".",
-    "    ",
-    "    The following parameter table entries are supported:",
-    "    ",
-    "     Entry           & Type        & Description      ",
-    "     name            & WORD        & Name of the variable",
-    "     out_name        & WORD        & New name of the variable",
-    "     param           & WORD        & Parameter identifier (GRIB1: code[.tabnum];  GRIB2: num[.cat[.dis]])",
-    "     out_param       & WORD        & New parameter identifier",
-    "     type            & WORD        & Data type (real or double)",
-    "     standard_name   & WORD        & As defined in the CF standard name table",
-    "     long_name       & STRING      & Describing the variable",
-    "     units           & STRING      & Specifying the units for the variable",
-    "     comment         & STRING      & Information concerning the variable",
-    "     cell_methods    & STRING      & Information concerning calculation of means or climatologies",
-    "     cell_measures   & STRING      & Indicates the names of the variables containing cell areas and volumes",
-    "     missing_value   & FLOAT       & Specifying how missing data will be identified",
-    "     valid_min       & FLOAT       & Minimum valid value",
-    "     valid_max       & FLOAT       & Maximum valid value",
-    "     ok_min_mean_abs & FLOAT       & Minimum absolute mean",
-    "     ok_max_mean_abs & FLOAT       & Maximum absolute mean",
-    "     factor          & FLOAT       & Scale factor",
-    "     delete          & INTEGER     & Set to 1 to delete variable",
-    "     convert         & INTEGER     & Set to 1 to convert the unit if necessary",
-    "    ",
-    "    Unsupported parameter table entries are stored as variable attributes.",
-    "    The search key for the variable depends on the operator. Use setpartabn to search variables by the name.",
-    "    This is typically used for NetCDF datasets. The operator setpartabp searches variables by the parameter ID.",
-    "",
-    "OPERATORS",
-    "    setpartabp  Set parameter table",
-    "                Search variables by the parameter identifier.",
-    "    setpartabn  Set parameter table",
-    "                Search variables by name.",
-    "",
-    "PARAMETER",
-    "    table    STRING   Parameter table file or name",
-    "    convert  STRING   Converts the units if necessary",
-    nullptr
-};
-
-static const char *SetHelp[] = {
-    "NAME",
-    "    setcodetab, setcode, setparam, setname, setunit, setlevel, setltype, ",
-    "    setmaxsteps - Set field info",
-    "",
-    "SYNOPSIS",
-    "    setcodetab,table  infile outfile",
-    "    setcode,code  infile outfile",
-    "    setparam,param  infile outfile",
-    "    setname,name  infile outfile",
-    "    setunit,unit  infile outfile",
-    "    setlevel,level  infile outfile",
-    "    setltype,ltype  infile outfile",
-    "    setmaxsteps,maxsteps  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module sets some field information. Depending on the chosen operator the ",
-    "    parameter table, code number, parameter identifier, variable name or level is set.",
-    "",
-    "OPERATORS",
-    "    setcodetab   Set parameter code table",
-    "                 Sets the parameter code table for all variables.",
-    "    setcode      Set code number",
-    "                 Sets the code number for all variables to the same given value.",
-    "    setparam     Set parameter identifier",
-    "                 Sets the parameter identifier of the first variable.",
-    "    setname      Set variable name",
-    "                 Sets the name of the first variable.",
-    "    setunit      Set variable unit",
-    "                 Sets the unit of the first variable.",
-    "    setlevel     Set level",
-    "                 Sets the first level of all variables.",
-    "    setltype     Set GRIB level type",
-    "                 Sets the GRIB level type of all variables.",
-    "    setmaxsteps  Set max timesteps",
-    "                 Sets maximum number of timesteps",
-    "",
-    "PARAMETER",
-    "    table     STRING   Parameter table file or name",
-    "    code      INTEGER  Code number",
-    "    param     STRING   Parameter identifier (GRIB1: code[.tabnum]; GRIB2: num[.cat[.dis]])",
-    "    name      STRING   Variable name",
-    "    level     FLOAT    New level",
-    "    ltype     INTEGER  GRIB level type",
-    "    maxsteps  INTEGER  Maximum number of timesteps",
-    nullptr
-};
-
-static const char *SettimeHelp[] = {
-    "NAME",
-    "    setdate, settime, setday, setmon, setyear, settunits, settaxis, settbounds, ",
-    "    setreftime, setcalendar, shifttime - Set time",
-    "",
-    "SYNOPSIS",
-    "    setdate,date  infile outfile",
-    "    settime,time  infile outfile",
-    "    setday,day  infile outfile",
-    "    setmon,month  infile outfile",
-    "    setyear,year  infile outfile",
-    "    settunits,units  infile outfile",
-    "    settaxis,date,time[,inc]  infile outfile",
-    "    settbounds,frequency  infile outfile",
-    "    setreftime,date,time[,units]  infile outfile",
-    "    setcalendar,calendar  infile outfile",
-    "    shifttime,shiftValue  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module sets the time axis or part of the time axis. Which part of the time axis is",
-    "    overwritten/created depends on the chosen operator. The number of time steps does not change.",
-    "",
-    "OPERATORS",
-    "    setdate      Set date",
-    "                 Sets the date in every timestep to the same given value.",
-    "    settime      Set time of the day",
-    "                 Sets the time in every timestep to the same given value.",
-    "    setday       Set day",
-    "                 Sets the day in every timestep to the same given value.",
-    "    setmon       Set month",
-    "                 Sets the month in every timestep to the same given value.",
-    "    setyear      Set year",
-    "                 Sets the year in every timestep to the same given value.",
-    "    settunits    Set time units",
-    "                 Sets the base units of a relative time axis.",
-    "    settaxis     Set time axis",
-    "                 Sets the time axis.",
-    "    settbounds   Set time bounds",
-    "                 Sets the time bounds.",
-    "    setreftime   Set reference time",
-    "                 Sets the reference time of a relative time axis.",
-    "    setcalendar  Set calendar",
-    "                 Sets the calendar attribute of a relative time axis.",
-    "    shifttime    Shift timesteps",
-    "                 Shifts all timesteps by the parameter shiftValue.",
-    "",
-    "PARAMETER",
-    "    day         INTEGER  Value of the new day",
-    "    month       INTEGER  Value of the new month",
-    "    year        INTEGER  Value of the new year",
-    "    units       STRING   Base units of the time axis (seconds, minutes, hours, days, months, years)",
-    "    date        STRING   Date (format: YYYY-MM-DD)",
-    "    time        STRING   Time (format: hh:mm:ss)",
-    "    inc         STRING   Optional increment (seconds, minutes, hours, days, months, years) [default: 1hour]",
-    "    frequency   STRING   Frequency of the time series (hour, day, month, year)",
-    "    calendar    STRING   Calendar (standard, proleptic_gregorian, 360_day, 365_day, 366_day)",
-    "    shiftValue  STRING   Shift value (e.g. -3hour)",
-    nullptr
-};
-
-static const char *ChangeHelp[] = {
-    "NAME",
-    "    chcode, chparam, chname, chunit, chlevel, chlevelc, chlevelv - ",
-    "    Change field header",
-    "",
-    "SYNOPSIS",
-    "    chcode,oldcode,newcode[,...]  infile outfile",
-    "    chparam,oldparam,newparam,...  infile outfile",
-    "    chname,oldname,newname,...  infile outfile",
-    "    chunit,oldunit,newunit,...  infile outfile",
-    "    chlevel,oldlev,newlev,...  infile outfile",
-    "    chlevelc,code,oldlev,newlev  infile outfile",
-    "    chlevelv,name,oldlev,newlev  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module reads fields from infile, changes some header values",
-    "    and writes the results to outfile. The kind of changes depends on ",
-    "    the chosen operator.",
-    "",
-    "OPERATORS",
-    "    chcode    Change code number",
-    "              Changes some user given code numbers to new user given values.",
-    "    chparam   Change parameter identifier",
-    "              Changes some user given parameter identifiers to new user given values.",
-    "    chname    Change variable or coordinate name",
-    "              Changes some user given variable or coordinate names to new user given names.",
-    "    chunit    Change variable unit",
-    "              Changes some user given variable units to new user given units.",
-    "    chlevel   Change level",
-    "              Changes some user given levels to new user given values.",
-    "    chlevelc  Change level of one code",
-    "              Changes one level of a user given code number.",
-    "    chlevelv  Change level of one variable",
-    "              Changes one level of a user given variable name.",
-    "",
-    "PARAMETER",
-    "    code                   INTEGER  Code number",
-    "    oldcode,newcode,...    INTEGER  Pairs of old and new code numbers",
-    "    oldparam,newparam,...  STRING   Pairs of old and new parameter identifiers",
-    "    name                   STRING   Variable name",
-    "    oldname,newname,...    STRING   Pairs of old and new variable names",
-    "    oldlev                 FLOAT    Old level",
-    "    newlev                 FLOAT    New level",
-    "    oldlev,newlev,...      FLOAT    Pairs of old and new levels",
-    nullptr
-};
-
-static const char *SetgridHelp[] = {
-    "NAME",
-    "    setgrid, setgridtype, setgridarea, setgridmask - Set grid information",
-    "",
-    "SYNOPSIS",
-    "    setgrid,grid  infile outfile",
-    "    setgridtype,gridtype  infile outfile",
-    "    setgridarea,gridarea  infile outfile",
-    "    setgridmask,gridmask  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module modifies the metadata of the horizontal grid. Depending on the chosen operator",
-    "    a new grid description is set, the coordinates are converted or the grid cell area is added.",
-    "",
-    "OPERATORS",
-    "    setgrid      Set grid",
-    "                 Sets a new grid description. The input fields need to have the same grid size as the size",
-    "                 of the target grid description.",
-    "    setgridtype  Set grid type",
-    "                 Sets the grid type of all input fields. The following grid types are available:",
-    "                 curvilinear "    "    Converts a regular grid to a curvilinear grid",
-    "                 unstructured"    "    Converts a regular or curvilinear grid to an unstructured grid",
-    "                 dereference "    "    Dereference a reference to a grid",
-    "                 regular     "    "    Linear interpolation of a reduced Gaussian grid to a regular Gaussian grid",
-    "                 regularnn   "    "    Nearest neighbor interpolation of a reduced Gaussian grid to a regular Gaussian grid",
-    "                 lonlat      "    "    Converts a regular lonlat grid stored as a curvilinear grid back to a lonlat grid",
-    "                 projection  "    "    Removes the geographical coordinates if projection parameter available",
-    "    setgridarea  Set grid cell area",
-    "                 Sets the grid cell area. The parameter gridarea is the path to a data file,",
-    "                 the first field is used as grid cell area. The input fields need to have the same",
-    "                 grid size as the grid cell area. The grid cell area is used to compute",
-    "                 the weights of each grid cell if needed by an operator, e.g. for fldmean.",
-    "    setgridmask  Set grid mask",
-    "                 Sets the grid mask. The parameter gridmask is the path to a data file,",
-    "                 the first field is used as the grid mask. The input fields need to have the same",
-    "                 grid size as the grid mask. The grid mask is used as the target grid mask for",
-    "                 remapping, e.g. for remapbil.",
-    "",
-    "PARAMETER",
-    "    grid      STRING  Grid description file or name",
-    "    gridtype  STRING  Grid type (curvilinear, unstructured, regular, lonlat, projection or dereference)",
-    "    gridarea  STRING  Data file, the first field is used as grid cell area",
-    "    gridmask  STRING  Data file, the first field is used as grid mask",
-    nullptr
-};
-
-static const char *SetzaxisHelp[] = {
-    "NAME",
-    "    setzaxis, genlevelbounds - Set z-axis information",
-    "",
-    "SYNOPSIS",
-    "    setzaxis,zaxis  infile outfile",
-    "    genlevelbounds[,zbot[,ztop]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module modifies the metadata of the vertical grid.",
-    "",
-    "OPERATORS",
-    "    setzaxis        Set z-axis",
-    "                    This operator sets the z-axis description of all variables with the same number of level as the new z-axis.",
-    "    genlevelbounds  Generate level bounds",
-    "                    Generates the layer bounds of the z-axis.",
-    "",
-    "PARAMETER",
-    "    zaxis  STRING  Z-axis description file or name of the target z-axis",
-    "    zbot   FLOAT   Specifying the bottom of the vertical column. Must have the same units as z-axis. ",
-    "    ztop   FLOAT   Specifying the top of the vertical column. Must have the same units as z-axis. ",
-    nullptr
-};
-
-static const char *InvertHelp[] = {
-    "NAME",
-    "    invertlat - Invert latitudes",
-    "",
-    "SYNOPSIS",
-    "    invertlat  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator inverts the latitudes of all fields on a rectilinear grid. ",
-    nullptr
-};
-
-static const char *InvertlevHelp[] = {
-    "NAME",
-    "    invertlev - Invert levels",
-    "",
-    "SYNOPSIS",
-    "    invertlev  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator inverts the levels of all 3D variables.",
-    nullptr
-};
-
-static const char *ShiftxyHelp[] = {
-    "NAME",
-    "    shiftx, shifty - Shift field",
-    "",
-    "SYNOPSIS",
-    "    <operator>,<nshift>,<cyclic>,<coord>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators to shift all fields in x or y direction.",
-    "    All fields need to have the same horizontal rectilinear or curvilinear grid.",
-    "",
-    "OPERATORS",
-    "    shiftx  Shift x",
-    "            Shifts all fields in x direction.",
-    "    shifty  Shift y",
-    "            Shifts all fields in y direction.",
-    "",
-    "PARAMETER",
-    "    nshift  INTEGER  Number of grid cells to shift (default: 1)",
-    "    cyclic  STRING   If set, cells are filled up cyclic (default: missing value)",
-    "    coord   STRING   If set, coordinates are also shifted",
-    nullptr
-};
-
-static const char *MaskregionHelp[] = {
-    "NAME",
-    "    maskregion - Mask regions",
-    "",
-    "SYNOPSIS",
-    "    maskregion,regions  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Masks different regions of the input fields.",
-    "    The grid cells inside a region are untouched, the cells outside are set to missing value.",
-    "    Considered are only those grid cells with the grid center inside the regions.",
-    "    All input fields must have the same horizontal grid.",
-    "    ",
-    "    Regions can be defined by the user via an ASCII file.",
-    "    Each region consists of the geographic coordinates of a convex polygon.",
-    "    Each line of a polygon description file contains the longitude and latitude of one point.",
-    "    Each polygon description file can contain one or more polygons separated by a line with the character \\&.",
-    "    ",
-    "    Predefined regions of countries can be specified via the country codes.",
-    "    A country is specified with dcw:<CountryCode>. Country codes can be combined with the plus sign.",
-    "",
-    "PARAMETER",
-    "    regions  STRING   Comma-separated list of ASCII formatted files with different regions",
-    nullptr
-};
-
-static const char *MaskboxHelp[] = {
-    "NAME",
-    "    masklonlatbox, maskindexbox - Mask a box",
-    "",
-    "SYNOPSIS",
-    "    masklonlatbox,lon1,lon2,lat1,lat2  infile outfile",
-    "    maskindexbox,idx1,idx2,idy1,idy2  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Masks grid cells inside a lon/lat or index box. The elements inside the box are untouched, the ",
-    "    elements outside are set to missing value. All input fields need to have the same horizontal grid.",
-    "    Use sellonlatbox or selindexbox if only the data inside the box are needed.",
-    "",
-    "OPERATORS",
-    "    masklonlatbox  Mask a longitude/latitude box",
-    "                   Masks grid cells inside a lon/lat box. The user must specify the longitude and latitude of the edges of the box.",
-    "                   Only those grid cells are considered whose grid center lies within the lon/lat box.",
-    "                   For rotated lon/lat grids the parameters must be specified in rotated coordinates.",
-    "    maskindexbox   Mask an index box",
-    "                   Masks grid cells within an index box. The user must specify the indices of the edges of the box.",
-    "                   The index of the left edge can be greater then the one of the right edge. Use negative indexing to",
-    "                   start from the end. The input grid must be a regular lon/lat or a 2D curvilinear grid.",
-    "",
-    "PARAMETER",
-    "    lon1  FLOAT    Western longitude",
-    "    lon2  FLOAT    Eastern longitude",
-    "    lat1  FLOAT    Southern or northern latitude",
-    "    lat2  FLOAT    Northern or southern latitude",
-    "    idx1  INTEGER  Index of first longitude",
-    "    idx2  INTEGER  Index of last longitude",
-    "    idy1  INTEGER  Index of first latitude",
-    "    idy2  INTEGER  Index of last latitude",
-    nullptr
-};
-
-static const char *SetboxHelp[] = {
-    "NAME",
-    "    setclonlatbox, setcindexbox - Set a box to constant",
-    "",
-    "SYNOPSIS",
-    "    setclonlatbox,c,lon1,lon2,lat1,lat2  infile outfile",
-    "    setcindexbox,c,idx1,idx2,idy1,idy2  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Sets a box of the rectangularly understood field to a constant value. The elements outside ",
-    "    the box are untouched, the elements inside are set to the given constant. All input fields ",
-    "    need to have the same horizontal grid.",
-    "",
-    "OPERATORS",
-    "    setclonlatbox  Set a longitude/latitude box to constant",
-    "                   Sets the values of a longitude/latitude box to a constant value. The ",
-    "                   user has to give the longitudes and latitudes of the edges of the box.",
-    "    setcindexbox   Set an index box to constant",
-    "                   Sets the values of an index box to a constant value. The user has to ",
-    "                   give the indices of the edges of the box. The index of the left edge ",
-    "                   can be greater than the one of the right edge.",
-    "",
-    "PARAMETER",
-    "    c     FLOAT    Constant",
-    "    lon1  FLOAT    Western longitude",
-    "    lon2  FLOAT    Eastern longitude",
-    "    lat1  FLOAT    Southern or northern latitude",
-    "    lat2  FLOAT    Northern or southern latitude",
-    "    idx1  INTEGER  Index of first longitude",
-    "    idx2  INTEGER  Index of last longitude",
-    "    idy1  INTEGER  Index of first latitude",
-    "    idy2  INTEGER  Index of last latitude",
-    nullptr
-};
-
-static const char *EnlargeHelp[] = {
-    "NAME",
-    "    enlarge - Enlarge fields",
-    "",
-    "SYNOPSIS",
-    "    enlarge,grid  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Enlarge all fields of infile to a user given horizontal grid. Normally only the last ",
-    "    field element is used for the enlargement. If however the input and output",
-    "    grid are regular lon/lat grids, a zonal or meridional enlargement is possible.",
-    "    Zonal enlargement takes place, if the xsize of the input field is 1 and ",
-    "    the ysize of both grids are the same. For meridional enlargement the ysize",
-    "    have to be 1 and the xsize of both grids should have the same size.",
-    "",
-    "PARAMETER",
-    "    grid  STRING  Target grid description file or name",
-    nullptr
-};
-
-static const char *SetmissHelp[] = {
-    "NAME",
-    "    setmissval, setctomiss, setmisstoc, setrtomiss, setvrange, setmisstonn, ",
-    "    setmisstodis - Set missing value",
-    "",
-    "SYNOPSIS",
-    "    setmissval,newmiss  infile outfile",
-    "    setctomiss,c  infile outfile",
-    "    setmisstoc,c  infile outfile",
-    "    setrtomiss,rmin,rmax  infile outfile",
-    "    setvrange,rmin,rmax  infile outfile",
-    "    setmisstonn  infile outfile",
-    "    setmisstodis[,neighbors]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module sets part of a field to missing value or missing values",
-    "    to a constant value. Which part of the field is set depends on the ",
-    "    chosen operator.",
-    "",
-    "OPERATORS",
-    "    setmissval    Set a new missing value",
-    "                           / newmiss   if i(t,x) EQ miss",
-    "                  o(t,x) = ",
-    "                           \\ i(t,x)    if i(t,x) NE miss",
-    "    setctomiss    Set constant to missing value",
-    "                           / miss   if i(t,x) EQ c",
-    "                  o(t,x) = ",
-    "                           \\ i(t,x) if i(t,x) NE c",
-    "    setmisstoc    Set missing value to constant",
-    "                           / c      if i(t,x) EQ miss",
-    "                  o(t,x) = ",
-    "                           \\ i(t,x) if i(t,x) NE miss",
-    "    setrtomiss    Set range to missing value",
-    "                           / miss   if i(t,x) GE rmin AND i(t,x) LE rmax",
-    "                  o(t,x) = ",
-    "                           \\ i(t,x) if i(t,x) LT rmin OR  i(t,x) GT rmax",
-    "    setvrange     Set valid range",
-    "                           / miss   if i(t,x) LT rmin OR  i(t,x) GT rmax",
-    "                  o(t,x) = ",
-    "                           \\ i(t,x) if i(t,x) GE rmin AND i(t,x) LE rmax",
-    "    setmisstonn   Set missing value to nearest neighbor",
-    "                  Set all missing values to the nearest non missing value.",
-    "                           / i(t,y) if i(t,x) EQ miss AND i(t,y) NE miss",
-    "                  o(t,x) = ",
-    "                           \\ i(t,x) if i(t,x) NE miss",
-    "    setmisstodis  Set missing value to distance-weighted average",
-    "                  Set all missing values to the distance-weighted average of the nearest non missing values.",
-    "                  The default number of nearest neighbors is 4.",
-    "",
-    "PARAMETER",
-    "    neighbors  INTEGER  Number of nearest neighbors",
-    "    newmiss    FLOAT    New missing value",
-    "    c          FLOAT    Constant",
-    "    rmin       FLOAT    Lower bound",
-    "    rmax       FLOAT    Upper bound",
-    nullptr
-};
-
-static const char *VertfillmissHelp[] = {
-    "NAME",
-    "    vertfillmiss - Vertical filling of missing values",
-    "",
-    "SYNOPSIS",
-    "    vertfillmiss[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator fills in vertical missing values.",
-    "    The method parameter can be used to select the filling method.",
-    "    The default method=nearest fills missing values with the nearest neighbor value.",
-    "    Other options are forward and backward to fill missing values by forward or backward propagation of values.",
-    "    Use the limit parameter to set the maximum number of consecutive missing values to fill and max_gaps to set the maximum number of gaps to fill.",
-    "",
-    "PARAMETER",
-    "    method    STRING   Fill method [nearest|linear|forward|backward] (default: nearest)",
-    "    limit     INTEGER  The maximum number of consecutive missing values to fill (default: all)",
-    "    max_gaps  INTEGER  The maximum number of gaps to fill (default: all)",
-    nullptr
-};
-
-static const char *TimfillmissHelp[] = {
-    "NAME",
-    "    timfillmiss - Temporal filling of missing values",
-    "",
-    "SYNOPSIS",
-    "    timfillmiss[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator fills in temporally missing values.",
-    "    The method parameter can be used to select the filling method.",
-    "    The default method=nearest fills missing values with the nearest neighbor value.",
-    "    Other options are forward and backward to fill missing values by forward or backward propagation of values.",
-    "    Use the limit parameter to set the maximum number of consecutive missing values to fill and max_gaps to set the maximum number of gaps to fill.",
-    "",
-    "PARAMETER",
-    "    method    STRING   Fill method [nearest|linear|forward|backward] (default: nearest)",
-    "    limit     INTEGER  The maximum number of consecutive missing values to fill (default: all)",
-    "    max_gaps  INTEGER  The maximum number of gaps to fill (default: all)",
-    nullptr
-};
-
-static const char *SetgridcellHelp[] = {
-    "NAME",
-    "    setgridcell - Set the value of a grid cell",
-    "",
-    "SYNOPSIS",
-    "    setgridcell,parameter  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator sets the value of the selected grid cells. The grid cells can be selected by a comma-separated list of grid cell indices",
-    "    or a mask. The mask is read from a data file, which may contain only one field. If no grid cells are selected, all values are set.",
-    "",
-    "PARAMETER",
-    "    value  FLOAT    Value of the grid cell",
-    "    cell   INTEGER  Comma-separated list of grid cell indices",
-    "    mask   STRING   Name of the data file which contains the mask",
-    nullptr
-};
-
-static const char *ExprHelp[] = {
-    "NAME",
-    "    expr, exprf, aexpr, aexprf - Evaluate expressions",
-    "",
-    "SYNOPSIS",
-    "    expr,instr  infile outfile",
-    "    exprf,filename  infile outfile",
-    "    aexpr,instr  infile outfile",
-    "    aexprf,filename  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module arithmetically processes every timestep of the input dataset.",
-    "    Each individual assignment statement have to end with a semi-colon.",
-    "    The special key _ALL_ is used as a template. A statement with a template is replaced for all variable names.",
-    "    Unlike regular variables, temporary variables are never written to the output stream.",
-    "    To define a temporary variable simply prefix the variable name with an underscore (e.g. _varname)",
-    "    when the variable is declared.",
-    "    ",
-    "    The following operators are supported:",
-    "    ",
-    "     Operator   & Meaning             & Example   & Result ",
-    "         =      & assignment          & x = y     & Assigns y to x",
-    "         +      & addition            & x + y     & Sum of x and y",
-    "         -      & subtraction         & x - y     & Difference of x and y    ",
-    "         *      & multiplication      & x * y     & Product of x and y ",
-    "         /      & division            & x / y     & Quotient of x and y",
-    "         ^      & exponentiation      & x ^y      & Exponentiates x with y ",
-    "         ==     & equal to            & x == y    &  1, if x equal to y; else 0",
-    "         !=     & not equal to        & x != y    &  1, if x not equal to y; else 0",
-    "         >      & greater than        & x > y     &  1, if x greater than y; else 0",
-    "         <      & less than           & x < y     &  1, if x less than y; else 0",
-    "         >=     & greater equal       & x >= y    &  1, if x greater equal y; else 0",
-    "         <=     & less equal          & x <= y    &  1, if x less equal y; else 0",
-    "         <=>    & less equal greater  & x <=> y   & -1, if x less y; 1, if x greater y; else 0 ",
-    "         &&     & logical AND         & x && y    &  1, if x and y not equal 0; else 0",
-    "         ||     & logical OR          & x || y    &  1, if x or y not equal 0; else 0",
-    "         !      & logical NOT         & !x        &  1, if x equal 0; else 0",
-    "         ?:     & ternary conditional & x ? y : z & y, if x not equal 0, else z ",
-    "    ",
-    "    The following functions are supported:",
-    "    ",
-    "    Math intrinsics:",
-    "    ",
-    "    abs(x)      "    "    Absolute value of x",
-    "    floor(x)    "    "    Round to largest integral value not greater than x",
-    "    ceil(x)     "    "    Round to smallest integral value not less than x",
-    "    float(x)    "    "    32-bit float value of x",
-    "    int(x)      "    "    Integer value of x",
-    "    nint(x)     "    "    Nearest integer value of x",
-    "    sqr(x)      "    "    Square of x",
-    "    sqrt(x)     "    "    Square Root of x",
-    "    exp(x)      "    "    Exponential of x",
-    "    ln(x)       "    "    Natural logarithm of x",
-    "    log10(x)    "    "    Base 10 logarithm of x",
-    "    sin(x)      "    "    Sine of x, where x is specified in radians",
-    "    cos(x)      "    "    Cosine of x, where x is specified in radians",
-    "    tan(x)      "    "    Tangent of x, where x is specified in radians",
-    "    asin(x)     "    "    Arc-sine of x, where x is specified in radians",
-    "    acos(x)     "    "    Arc-cosine of x, where x is specified in radians",
-    "    atan(x)     "    "    Arc-tangent of x, where x is specified in radians",
-    "    sinh(x)     "    "    Hyperbolic sine of x, where x is specified in radians",
-    "    cosh(x)     "    "    Hyperbolic cosine of x, where x is specified in radians",
-    "    tanh(x)     "    "    Hyperbolic tangent of x, where x is specified in radians",
-    "    asinh(x)    "    "    Inverse hyperbolic sine of x, where x is specified in radians",
-    "    acosh(x)    "    "    Inverse hyperbolic cosine of x, where x is specified in radians",
-    "    atanh(x)    "    "    Inverse hyperbolic tangent of x, where x is specified in radians",
-    "    rad(x)      "    "    Convert x from degrees to radians",
-    "    deg(x)      "    "    Convert x from radians to degrees",
-    "    rand(x)     "    "    Replace x by pseudo-random numbers in the range of 0 to 1 ",
-    "    isMissval(x)"    "    Returns 1 where x is missing",
-    "    ",
-    "    mod(x,y)    "    "    Floating-point remainder of x/ y",
-    "    min(x,y)    "    "    Minimum value of x and y",
-    "    max(x,y)    "    "    Maximum value of x and y",
-    "    pow(x,y)    "    "    Power function",
-    "    hypot(x,y)  "    "    Euclidean distance function, sqrt(x*x + y*y)",
-    "    atan2(x,y)  "    "    Arc tangent function of y/x, using signs to determine quadrants ",
-    "    ",
-    "    Coordinates:",
-    "    ",
-    "    clon(x)    "    "    Longitude coordinate of x (available only if x has geographical coordinates)",
-    "    clat(x)    "    "    Latitude coordinate of x (available only if x has geographical coordinates)",
-    "    gridarea(x)"    "    Grid cell area of x (available only if x has geographical coordinates)",
-    "    gridindex(x)"    "    Grid cell indices of x",
-    "    clev(x)    "    "    Level coordinate of x (0, if x is a 2D surface variable)",
-    "    clevidx(x) "    "    Level index of x (0, if x is a 2D surface variable)",
-    "    cthickness(x)"    "    Layer thickness, upper minus lower level bound of x (1, if level bounds are missing)",
-    "    ctimestep()"    "    Timestep number (1 to N)",
-    "    cdate()    "    "    Verification date as YYYYMMDD",
-    "    ctime()    "    "    Verification time as HHMMSS.millisecond",
-    "    cdeltat()  "    "    Difference between current and last timestep in seconds",
-    "    cday()     "    "    Day as DD",
-    "    cmonth()   "    "    Month as MM",
-    "    cyear()    "    "    Year as YYYY",
-    "    csecond()  "    "    Second as SS.millisecond",
-    "    cminute()  "    "    Minute as MM",
-    "    chour()    "    "    Hour as HH",
-    "    ",
-    "    Constants:",
-    "    ",
-    "    ngp(x)    "    "    Number of horizontal grid points",
-    "    nlev(x)   "    "    Number of vertical levels",
-    "    size(x)   "    "    Total number of elements (ngp(x)*nlev(x))",
-    "    missval(x)"    "    Returns the missing value of variable x",
-    "    ",
-    "    Statistical values over a field:",
-    "    ",
-    "    fldmin(x), fldmax(x), fldrange(x), fldsum(x), fldmean(x), fldavg(x), fldstd(x), fldstd1(x),",
-    "    fldvar(x), fldvar1(x), fldskew(x), fldkurt(x), fldmedian(x)",
-    "    ",
-    "    Zonal statistical values for regular 2D grids:",
-    "    ",
-    "    zonmin(x), zonmax(x), zonrange(x), zonsum(x), zonmean(x), zonavg(x), zonstd(x), zonstd1(x),",
-    "    zonvar(x), zonvar1(x), zonskew(x), zonkurt(x), zonmedian(x)",
-    "    ",
-    "    Vertical statistical values:",
-    "    ",
-    "    vertmin(x), vertmax(x), vertrange(x), vertsum(x), vertmean(x), vertavg(x), vertstd(x), vertstd1(x),",
-    "    vertvar(x), vertvar1(x)",
-    "    ",
-    "    Miscellaneous:",
-    "    ",
-    "    sellevel(x,k)          "    "    Select level k of variable x",
-    "    sellevidx(x,k)         "    "    Select level index k of variable x",
-    "    sellevelrange(x,k1,k2) "    "    Select all levels of variable x in the range k1 to k2",
-    "    sellevidxrange(x,k1,k2)"    "    Select all level indices of variable x in the range k1 to k2",
-    "    remove(x)              "    "    Remove variable x from output stream",
-    "    ",
-    "",
-    "OPERATORS",
-    "    expr    Evaluate expressions",
-    "            The processing instructions are read from the parameter.",
-    "    exprf   Evaluate expressions script",
-    "            Contrary to expr the processing instructions are read from a file.",
-    "    aexpr   Evaluate expressions and append results",
-    "            Same as expr, but keep input variables and append results",
-    "    aexprf  Evaluate expression script and append results",
-    "            Same as exprf, but keep input variables and append results",
-    "",
-    "PARAMETER",
-    "    instr     STRING  Processing instructions (need to be 'quoted' in most cases)",
-    "    filename  STRING  File with processing instructions",
-    "",
-    "NOTE",
-    "    If the input stream contains duplicate entries of the same variable name then the last one is used.",
-    nullptr
-};
-
-static const char *MathHelp[] = {
-    "NAME",
-    "    abs, int, nint, pow, sqr, sqrt, exp, ln, log10, sin, cos, tan, asin, acos, ",
-    "    atan, reci, not - Mathematical functions",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains some standard mathematical functions.",
-    "    All trigonometric functions calculate with radians.",
-    "",
-    "OPERATORS",
-    "    abs    Absolute value",
-    "           o(t,x) = abs(i(t,x))",
-    "    int    Integer value",
-    "           o(t,x) = int(i(t,x))",
-    "    nint   Nearest integer value",
-    "           o(t,x) = nint(i(t,x))",
-    "    pow    Power",
-    "           o(t,x) = i(t,x)^y",
-    "    sqr    Square",
-    "           o(t,x) = i(t,x)^2",
-    "    sqrt   Square root",
-    "           o(t,x) = sqrt(i(t,x))",
-    "    exp    Exponential",
-    "           o(t,x) = e^i(t,x)",
-    "    ln     Natural logarithm",
-    "           o(t,x) = ln(i(t,x))",
-    "    log10  Base 10 logarithm",
-    "           o(t,x) = log10(i(t,x))",
-    "    sin    Sine",
-    "           o(t,x) = sin(i(t,x))",
-    "    cos    Cosine",
-    "           o(t,x) = cos(i(t,x))",
-    "    tan    Tangent",
-    "           o(t,x) = tan(i(t,x))",
-    "    asin   Arc sine",
-    "           o(t,x) = asin(i(t,x))",
-    "    acos   Arc cosine",
-    "           o(t,x) = acos(i(t,x))",
-    "    atan   Arc tangent",
-    "           o(t,x) = atan(i(t,x))",
-    "    reci   Reciprocal value",
-    "           o(t,x) = 1 / i(t,x)",
-    "    not    Logical NOT",
-    "           o(t,x) = 1, if x equal 0; else 0",
-    nullptr
-};
-
-static const char *ArithcHelp[] = {
-    "NAME",
-    "    addc, subc, mulc, divc, minc, maxc - Arithmetic with a constant",
-    "",
-    "SYNOPSIS",
-    "    <operator>,c  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic with all field elements of a dataset and ",
-    "    a constant. The fields in outfile inherit the meta data from infile.",
-    "",
-    "OPERATORS",
-    "    addc  Add a constant",
-    "          o(t,x) = i(t,x) + c",
-    "    subc  Subtract a constant",
-    "          o(t,x) = i(t,x) - c",
-    "    mulc  Multiply with a constant",
-    "          o(t,x) = i(t,x) * c",
-    "    divc  Divide by a constant",
-    "          o(t,x) = i(t,x) / c",
-    "    minc  Minimum of a field and a constant",
-    "          o(t,x) = min(i(t,x), c)",
-    "    maxc  Maximum of a field and a constant",
-    "          o(t,x) = max(i(t,x), c)",
-    "",
-    "PARAMETER",
-    "    c  FLOAT  Constant",
-    nullptr
-};
-
-static const char *ArithHelp[] = {
-    "NAME",
-    "    add, sub, mul, div, min, max, atan2 - Arithmetic on two datasets",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of two datasets.",
-    "    The number of fields in infile1 should be the same as in infile2.",
-    "    The fields in outfile inherit the meta data from infile1.",
-    "    All operators in this module simply process one field after the other from the two input files.",
-    "    Neither the order of the variables nor the date is checked.",
-    "    One of the input files can contain only one timestep or one variable.",
-    "",
-    "OPERATORS",
-    "    add    Add two fields",
-    "           o(t,x) = i_1(t,x) + i_2(t,x)",
-    "    sub    Subtract two fields",
-    "           o(t,x) = i_1(t,x) - i_2(t,x)",
-    "    mul    Multiply two fields",
-    "           o(t,x) = i_1(t,x) * i_2(t,x)",
-    "    div    Divide two fields",
-    "           o(t,x) = i_1(t,x) / i_2(t,x)",
-    "    min    Minimum of two fields",
-    "           o(t,x) = min(i_1(t,x), i_2(t,x))",
-    "    max    Maximum of two fields",
-    "           o(t,x) = max(i_1(t,x), i_2(t,x))",
-    "    atan2  Arc tangent of two fields",
-    "           The atan2 operator calculates the arc tangent of two fields. The result is",
-    "           in radians, which is between -PI and PI (inclusive).",
-    "           ",
-    "           o(t,x) = atan2(i_1(t,x), i_2(t,x))",
-    nullptr
-};
-
-static const char *DayarithHelp[] = {
-    "NAME",
-    "    dayadd, daysub, daymul, daydiv - Daily arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep with the same day, month and year.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same day, month and year is used.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module DAYSTAT.",
-    "",
-    "OPERATORS",
-    "    dayadd  Add daily time series",
-    "            Adds a time series and a daily time series.",
-    "    daysub  Subtract daily time series",
-    "            Subtracts a time series and a daily time series.",
-    "    daymul  Multiply daily time series",
-    "            Multiplies a time series and a daily time series.",
-    "    daydiv  Divide daily time series",
-    "            Divides a time series and a daily time series.",
-    nullptr
-};
-
-static const char *MonarithHelp[] = {
-    "NAME",
-    "    monadd, monsub, monmul, mondiv - Monthly arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep with the same month and year.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same month and year is used.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module MONSTAT.",
-    "",
-    "OPERATORS",
-    "    monadd  Add monthly time series",
-    "            Adds a time series and a monthly time series.",
-    "    monsub  Subtract monthly time series",
-    "            Subtracts a time series and a monthly time series.",
-    "    monmul  Multiply monthly time series",
-    "            Multiplies a time series and a monthly time series.",
-    "    mondiv  Divide monthly time series",
-    "            Divides a time series and a monthly time series.",
-    nullptr
-};
-
-static const char *YeararithHelp[] = {
-    "NAME",
-    "    yearadd, yearsub, yearmul, yeardiv - Yearly arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep",
-    "    with the same year. For each field in infile1 the corresponding",
-    "    field of the timestep in infile2 with the same year is used.",
-    "    The header information in infile1 have to be the same as in infile2.",
-    "    Usually infile2 is generated by an operator of the module YEARSTAT.",
-    "",
-    "OPERATORS",
-    "    yearadd  Add yearly time series",
-    "             Adds a time series and a yearly time series.",
-    "    yearsub  Subtract yearly time series",
-    "             Subtracts a time series and a yearly time series.",
-    "    yearmul  Multiply yearly time series",
-    "             Multiplies a time series and a yearly time series.",
-    "    yeardiv  Divide yearly time series",
-    "             Divides a time series and a yearly time series.",
-    nullptr
-};
-
-static const char *YhourarithHelp[] = {
-    "NAME",
-    "    yhouradd, yhoursub, yhourmul, yhourdiv - Multi-year hourly arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep with the same hour and day of year.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same hour and day of year is used.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module YHOURSTAT.",
-    "",
-    "OPERATORS",
-    "    yhouradd  Add multi-year hourly time series",
-    "              Adds a time series and a multi-year hourly time series.",
-    "    yhoursub  Subtract multi-year hourly time series",
-    "              Subtracts a time series and a multi-year hourly time series.",
-    "    yhourmul  Multiply multi-year hourly time series",
-    "              Multiplies a time series and a multi-year hourly time series.",
-    "    yhourdiv  Divide multi-year hourly time series",
-    "              Divides a time series and a multi-year hourly time series.",
-    nullptr
-};
-
-static const char *YdayarithHelp[] = {
-    "NAME",
-    "    ydayadd, ydaysub, ydaymul, ydaydiv - Multi-year daily arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep with the same day of year.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same day of year is used.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module YDAYSTAT.",
-    "",
-    "OPERATORS",
-    "    ydayadd  Add multi-year daily time series",
-    "             Adds a time series and a multi-year daily time series.",
-    "    ydaysub  Subtract multi-year daily time series",
-    "             Subtracts a time series and a multi-year daily time series.",
-    "    ydaymul  Multiply multi-year daily time series",
-    "             Multiplies a time series and a multi-year daily time series.",
-    "    ydaydiv  Divide multi-year daily time series",
-    "             Divides a time series and a multi-year daily time series.",
-    nullptr
-};
-
-static const char *YmonarithHelp[] = {
-    "NAME",
-    "    ymonadd, ymonsub, ymonmul, ymondiv - Multi-year monthly arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep with the same month of year.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same month of year is used.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module YMONSTAT.",
-    "",
-    "OPERATORS",
-    "    ymonadd  Add multi-year monthly time series",
-    "             Adds a time series and a multi-year monthly time series.",
-    "    ymonsub  Subtract multi-year monthly time series",
-    "             Subtracts a time series and a multi-year monthly time series.",
-    "    ymonmul  Multiply multi-year monthly time series",
-    "             Multiplies a time series with a multi-year monthly time series.",
-    "    ymondiv  Divide multi-year monthly time series",
-    "             Divides a time series by a multi-year monthly time series.",
-    nullptr
-};
-
-static const char *YseasarithHelp[] = {
-    "NAME",
-    "    yseasadd, yseassub, yseasmul, yseasdiv - Multi-year seasonal arithmetic",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs simple arithmetic of a time series and one timestep with the same season.",
-    "    For each field in infile1 the corresponding field of the timestep in infile2 with the same season is used.",
-    "    The input files need to have the same structure with the same variables.",
-    "    Usually infile2 is generated by an operator of the module YSEASSTAT.",
-    "",
-    "OPERATORS",
-    "    yseasadd  Add multi-year seasonal time series",
-    "              Adds a time series and a multi-year seasonal time series.",
-    "    yseassub  Subtract multi-year seasonal time series",
-    "              Subtracts a time series and a multi-year seasonal time series.",
-    "    yseasmul  Multiply multi-year seasonal time series",
-    "              Multiplies a time series and a multi-year seasonal time series.",
-    "    yseasdiv  Divide multi-year seasonal time series",
-    "              Divides a time series and a multi-year seasonal time series.",
-    nullptr
-};
-
-static const char *ArithdaysHelp[] = {
-    "NAME",
-    "    muldpm, divdpm, muldpy, divdpy - Arithmetic with days",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module multiplies or divides each timestep of a dataset with the corresponding",
-    "    days per month or days per year. The result of these functions depends on the used",
-    "    calendar of the input data.",
-    "",
-    "OPERATORS",
-    "    muldpm  Multiply with days per month",
-    "            o(t,x) = i(t,x) * days_per_month",
-    "    divdpm  Divide by days per month",
-    "            o(t,x) = i(t,x) / days_per_month",
-    "    muldpy  Multiply with days per year",
-    "            o(t,x) = i(t,x) * days_per_year",
-    "    divdpy  Divide by days per year",
-    "            o(t,x) = i(t,x) / days_per_year",
-    nullptr
-};
-
-static const char *ArithlatHelp[] = {
-    "NAME",
-    "    mulcoslat, divcoslat - Arithmetic with latitude",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module multiplies or divides each field element with the cosine of the latitude.",
-    "",
-    "OPERATORS",
-    "    mulcoslat  Multiply with the cosine of the latitude",
-    "               o(t,x) = i(t,x) * cos(latitude(x))",
-    "    divcoslat  Divide by cosine of the latitude",
-    "               o(t,x) = i(t,x) / cos(latitude(x))",
-    nullptr
-};
-
-static const char *TimcumsumHelp[] = {
-    "NAME",
-    "    timcumsum - Cumulative sum over all timesteps",
-    "",
-    "SYNOPSIS",
-    "    timcumsum  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    The timcumsum operator calculates the cumulative sum over all timesteps.",
-    "    Missing values are treated as numeric zero when summing.",
-    "    ",
-    "    o(t,x) = sum{i(t',x), 0<t'<=t}",
-    nullptr
-};
-
-static const char *ConsecstatHelp[] = {
-    "NAME",
-    "    consecsum, consects - Consecute timestep periods",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes periods over all timesteps in infile where a",
-    "    certain property is valid. The property can be chosen by creating a mask from",
-    "    the original data, which is the expected input format for operators of this",
-    "    module. Depending on the operator full information about each period or",
-    "    just its length and ending date are computed.",
-    "",
-    "OPERATORS",
-    "    consecsum  Consecutive Sum",
-    "               This operator computes periods of consecutive timesteps similar to a",
-    "               runsum, but periods are finished, when the mask value is 0. That way",
-    "               multiple periods can be found. Timesteps from the input are preserved. Missing",
-    "               values are handled like 0, i.e. finish periods of consecutive timesteps.",
-    "    consects   Consecutive Timesteps",
-    "               In contrast to the operator above consects only computes the length of each",
-    "               period together with its last timestep. To be able to perform statistical",
-    "               analysis like min, max or mean, everything else is set to missing value.",
-    nullptr
-};
-
-static const char *VarsstatHelp[] = {
-    "NAME",
-    "    varsmin, varsmax, varsrange, varssum, varsmean, varsavg, varsstd, varsstd1, ",
-    "    varsvar, varsvar1 - Statistical values over all variables",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over all variables for each timestep.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance or",
-    "    standard deviation is written to outfile.",
-    "    All input variables need to have the same gridsize and the same number of levels.",
-    "",
-    "OPERATORS",
-    "    varsmin    Variables minimum",
-    "               For every timestep the minimum over all variables is computed.",
-    "    varsmax    Variables maximum",
-    "               For every timestep the maximum over all variables is computed.",
-    "    varsrange  Variables range",
-    "               For every timestep the range over all variables is computed.",
-    "    varssum    Variables sum",
-    "               For every timestep the sum over all variables is computed.",
-    "    varsmean   Variables mean",
-    "               For every timestep the mean over all variables is computed.",
-    "    varsavg    Variables average",
-    "               For every timestep the average over all variables is computed.",
-    "    varsstd    Variables standard deviation",
-    "               For every timestep the standard deviation over all variables is computed. Normalize by n.",
-    "    varsstd1   Variables standard deviation (n-1)",
-    "               For every timestep the standard deviation over all variables is computed. Normalize by (n-1).",
-    "    varsvar    Variables variance",
-    "               For every timestep the variance over all variables is computed. Normalize by n.",
-    "    varsvar1   Variables variance (n-1)",
-    "               For every timestep the variance over all variables is computed. Normalize by (n-1).",
-    nullptr
-};
-
-static const char *EnsstatHelp[] = {
-    "NAME",
-    "    ensmin, ensmax, ensrange, enssum, ensmean, ensavg, ensstd, ensstd1, ensvar, ",
-    "    ensvar1, ensskew, enskurt, ensmedian, enspctl - ",
-    "    Statistical values over an ensemble",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infiles outfile",
-    "    enspctl,p  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over an ensemble of input files.",
-    "    Depending on the chosen operator, the minimum, maximum, range, sum, average, standard deviation, variance,",
-    "    skewness, kurtosis, median or a certain percentile over all input files is written to outfile.",
-    "    All input files need to have the same structure with the same variables.",
-    "    The date information of a timestep in outfile is the date of the first input file.",
-    "",
-    "OPERATORS",
-    "    ensmin     Ensemble minimum",
-    "               o(t,x) = min{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensmax     Ensemble maximum",
-    "               o(t,x) = max{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensrange   Ensemble range",
-    "               o(t,x) = range{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    enssum     Ensemble sum",
-    "               o(t,x) = sum{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensmean    Ensemble mean",
-    "               o(t,x) = mean{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensavg     Ensemble average",
-    "               o(t,x) = avg{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensstd     Ensemble standard deviation",
-    "               Normalize by n.",
-    "               ",
-    "               o(t,x) = std{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensstd1    Ensemble standard deviation (n-1)",
-    "               Normalize by (n-1).",
-    "               ",
-    "               o(t,x) = std1{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensvar     Ensemble variance",
-    "               Normalize by n.",
-    "               ",
-    "               o(t,x) = var{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensvar1    Ensemble variance (n-1)",
-    "               Normalize by (n-1).",
-    "               ",
-    "               o(t,x) = var1{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensskew    Ensemble skewness",
-    "               o(t,x) = skew{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    enskurt    Ensemble kurtosis",
-    "               o(t,x) = kurt{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    ensmedian  Ensemble median",
-    "               o(t,x) = median{i1(t,x), i2(t,x), ..., in(t,x)}",
-    "    enspctl    Ensemble percentiles",
-    "               o(t,x) = pth percentile {i1(t,x), i2(t,x), ..., in(t,x)}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "NOTE",
-    "    Operators of this module need to open all input files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *Ensstat2Help[] = {
-    "NAME",
-    "    ensrkhistspace, ensrkhisttime, ensroc - Statistical values over an ensemble",
-    "",
-    "SYNOPSIS",
-    "    <operator>  obsfile ensfiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over the ensemble of ensfiles using",
-    "    obsfile as a reference. Depending on the operator a ranked Histogram or ",
-    "    a roc-curve over all Ensembles ensfiles",
-    "    with reference to obsfile is written to outfile. ",
-    "    The date and grid information of a timestep in outfile is the date of the ",
-    "    first input file. Thus all input files are required to have the same structure in ",
-    "    terms of the gridsize, variable definitions and number of timesteps. ",
-    "    ",
-    "    All Operators in this module use obsfile as the reference (for instance ",
-    "    an observation) whereas ensfiles are understood as an ensemble consisting ",
-    "    of n (where n is the number of ensfiles) members. ",
-    "    ",
-    "    The operators ensrkhistspace and ensrkhisttime compute Ranked Histograms. ",
-    "    Therefor the vertical axis is utilized as the Histogram axis, which prohibits",
-    "    the use of files containing more than one level. The histogram axis has ",
-    "    nensfiles+1 bins with level 0 containing for each grid point the number of ",
-    "    observations being smaller as all ensembles and level nensfiles+1 indicating",
-    "    the number of observations being larger than all ensembles. ",
-    "    ",
-    "    ensrkhistspace computes a ranked histogram at each timestep reducing each ",
-    "    horizontal grid to a 1x1 grid and keeping the time axis as in obsfile. ",
-    "    Contrary ensrkhistspace  computes a histogram at each grid point keeping the ",
-    "    horizontal grid for each variable and reducing the time-axis. The time information",
-    "    is that from the last timestep in obsfile. ",
-    "",
-    "OPERATORS",
-    "    ensrkhistspace  Ranked Histogram averaged over time",
-    "    ensrkhisttime   Ranked Histogram averaged over space",
-    "    ensroc          Ensemble Receiver Operating characteristics",
-    nullptr
-};
-
-static const char *EnsvalHelp[] = {
-    "NAME",
-    "    enscrps, ensbrs - Ensemble validation tools",
-    "",
-    "SYNOPSIS",
-    "    enscrps  rfile infiles outfilebase",
-    "    ensbrs,x  rfile infiles outfilebase",
-    "",
-    "DESCRIPTION",
-    "    This module computes ensemble validation scores and their decomposition such as ",
-    "    the Brier and cumulative ranked probability score (CRPS). ",
-    "    The first file is used as a reference it can be a climatology, observation or ",
-    "    reanalysis against which the skill of the ensembles given in infiles is measured. ",
-    "    Depending on the operator a number of output files is generated each containing ",
-    "    the skill score and its decomposition corresponding to the operator. ",
-    "    The output is averaged  over horizontal fields using appropriate weights ",
-    "    for each level and timestep in rfile. ",
-    "    ",
-    "    All input files need to have the same structure with the same variables.",
-    "    The date information of a timestep in outfile is the date of the first input file.",
-    "    The output files are named as ",
-    "    <outfilebase>.<type>.<filesuffix> where <type> depends on the ",
-    "    operator and <filesuffix> is determined from the output file type. ",
-    "    There are three output files for operator enscrps and four output files ",
-    "    for operator ensbrs.",
-    "    ",
-    "    The CRPS and its decomposition into Reliability and the potential ",
-    "    CRPS are calculated by an appropriate averaging over the field ",
-    "    members (note, that the CRPS does *not* average linearly). ",
-    "    In the three output files ",
-    "    <type> has the following meaning:",
-    "    crps for the CRPS, reli for the reliability ",
-    "    and crpspot for the potential crps. The relation ",
-    "    CRPS = CRPS_{pot} + RELI",
-    "    holds. 	  ",
-    "    ",
-    "    The Brier score of the Ensemble given by infiles with respect to the ",
-    "    reference given in rfile and the threshold x is calculated. ",
-    "    In the four output files <type> has the following meaning: ",
-    "    brs for the Brier score wrt threshold  x; ",
-    "    brsreli for the Brier score reliability wrt threshold x;",
-    "    brsreso for the Brier score resolution wrt threshold x;",
-    "    brsunct for the Brier score uncertainty wrt threshold x.",
-    "    In analogy to the CRPS the following relation holds:",
-    "    BRS(x) = RELI(x)-RESO(x)+ UNCT(x).",
-    "    ",
-    "    The implementation of the decomposition of the CRPS and Brier Score follows ",
-    "      Hans Hersbach (2000): Decomposition of the Continuous Ranked Probability ",
-    "      Score for Ensemble Prediction Systems, in: Weather and Forecasting (15) ",
-    "      pp. 559-570. ",
-    "    ",
-    "    The CRPS code decomposition has been verified against the CRAN - ensemble ",
-    "    validation package from R. Differences occur when grid-cell area is not ",
-    "    uniform as the implementation in R does not account for that. ",
-    "    ",
-    "",
-    "OPERATORS",
-    "    enscrps  Ensemble CRPS and decomposition",
-    "    ensbrs   Ensemble Brier score",
-    "             Ensemble Brier Score and Decomposition",
-    nullptr
-};
-
-static const char *FldstatHelp[] = {
-    "NAME",
-    "    fldmin, fldmax, fldrange, fldsum, fldint, fldmean, fldavg, fldstd, fldstd1, ",
-    "    fldvar, fldvar1, fldskew, fldkurt, fldmedian, fldcount, fldpctl - ",
-    "    Statistical values over a field",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "    fldint,weights  infile outfile",
-    "    fldmean,weights  infile outfile",
-    "    fldavg,weights  infile outfile",
-    "    fldstd,weights  infile outfile",
-    "    fldstd1,weights  infile outfile",
-    "    fldvar,weights  infile outfile",
-    "    fldvar1,weights  infile outfile",
-    "    fldpctl,p  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values of all input fields. A field is a horizontal layer of a data variable.",
-    "    Depending on the chosen operator, the minimum, maximum, range, sum, integral, average, standard deviation, variance,",
-    "    skewness, kurtosis, median or a certain percentile of the field is written to outfile.",
-    "",
-    "OPERATORS",
-    "    fldmin     Field minimum",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = min{i(t,x'), x_1<x'<=x_n}",
-    "    fldmax     Field maximum",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = max{i(t,x'), x_1<x'<=x_n}",
-    "    fldrange   Field range",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = range{i(t,x'), x_1<x'<=x_n}",
-    "    fldsum     Field sum",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = sum{i(t,x'), x_1<x'<=x_n}",
-    "    fldint     Field integral",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = sum{i(t,x')*cellarea(x'), x_1<x'<=x_n}",
-    "    fldmean    Field mean",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = mean{i(t,x'), x_1<x'<=x_n}",
-    "               weighted by area weights obtained by the input field.",
-    "    fldavg     Field average",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = avg{i(t,x'), x_1<x'<=x_n}",
-    "               weighted by area weights obtained by the input field.",
-    "    fldstd     Field standard deviation",
-    "               Normalize by n. For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = std{i(t,x'), x_1<x'<=x_n}",
-    "               weighted by area weights obtained by the input field.",
-    "    fldstd1    Field standard deviation (n-1)",
-    "               Normalize by (n-1). For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = std1{i(t,x'), x_1<x'<=x_n}",
-    "               weighted by area weights obtained by the input field.",
-    "    fldvar     Field variance",
-    "               Normalize by n. For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = var{i(t,x'), x_1<x'<=x_n}",
-    "               weighted by area weights obtained by the input field.",
-    "    fldvar1    Field variance (n-1)",
-    "               Normalize by (n-1). For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = var1{i(t,x'), x_1<x'<=x_n}",
-    "               weighted by area weights obtained by the input field.",
-    "    fldskew    Field skewness",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = skew{i(t,x'), x_1<x'<=x_n}",
-    "    fldkurt    Field kurtosis",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = kurt{i(t,x'), x_1<x'<=x_n}",
-    "    fldmedian  Field median",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = median{i(t,x'), x_1<x'<=x_n}",
-    "    fldcount   Field count",
-    "               Number of non-missing values of the field.",
-    "    fldpctl    Field percentiles",
-    "               For every gridpoint x_1, ..., x_n of the same field it is:",
-    "               ",
-    "               o(t,1) = pth percentile {i(t,x'), x_1<x'<=x_n}",
-    "",
-    "PARAMETER",
-    "    weights  BOOL   weights=FALSE disables weighting by grid cell area [default: weights=TRUE]",
-    "    p        FLOAT  Percentile number in {0, ..., 100}",
-    nullptr
-};
-
-static const char *ZonstatHelp[] = {
-    "NAME",
-    "    zonmin, zonmax, zonrange, zonsum, zonmean, zonavg, zonstd, zonstd1, zonvar, ",
-    "    zonvar1, zonskew, zonkurt, zonmedian, zonpctl - Zonal statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "    zonmean[,zonaldes]  infile outfile",
-    "    zonpctl,p  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes zonal statistical values of the input fields.",
-    "    Depending on the chosen operator, the zonal minimum, maximum, range, sum, average, standard deviation, variance,",
-    "    skewness, kurtosis, median or a certain percentile of the field is written to outfile.",
-    "    Operators of this module require all variables on the same regular lon/lat grid.",
-    "    Only the zonal mean (zonmean) can be calculated for data on an unstructured grid if the latitude bins are",
-    "    defined with the optional parameter zonaldes.",
-    "",
-    "OPERATORS",
-    "    zonmin     Zonal minimum",
-    "               For every latitude the minimum over all longitudes is computed.",
-    "    zonmax     Zonal maximum",
-    "               For every latitude the maximum over all longitudes is computed.",
-    "    zonrange   Zonal range",
-    "               For every latitude the range over all longitudes is computed.",
-    "    zonsum     Zonal sum",
-    "               For every latitude the sum over all longitudes is computed.",
-    "    zonmean    Zonal mean",
-    "               For every latitude the mean over all longitudes is computed.",
-    "               Use the optional parameter zonaldes for data on an unstructured grid.",
-    "    zonavg     Zonal average",
-    "               For every latitude the average over all longitudes is computed.",
-    "    zonstd     Zonal standard deviation",
-    "               For every latitude the standard deviation over all longitudes is computed. Normalize by n.",
-    "    zonstd1    Zonal standard deviation (n-1)",
-    "               For every latitude the standard deviation over all longitudes is computed. Normalize by (n-1). ",
-    "    zonvar     Zonal variance",
-    "               For every latitude the variance over all longitudes is computed. Normalize by n.",
-    "    zonvar1    Zonal variance (n-1)",
-    "               For every latitude the variance over all longitudes is computed. Normalize by (n-1).",
-    "    zonskew    Zonal skewness",
-    "               For every latitude the skewness over all longitudes is computed.",
-    "    zonkurt    Zonal kurtosis",
-    "               For every latitude the kurtosis over all longitudes is computed.",
-    "    zonmedian  Zonal median",
-    "               For every latitude the median over all longitudes is computed.",
-    "    zonpctl    Zonal percentiles",
-    "               For every latitude the pth percentile over all longitudes is computed.",
-    "",
-    "PARAMETER",
-    "    p         FLOAT   Percentile number in {0, ..., 100}",
-    "    zonaldes  STRING  Description of the zonal latitude bins needed for data on an unstructured grid. A predefined zonal description is zonal_<DY>. DY is the increment of the latitudes in degrees.",
-    nullptr
-};
-
-static const char *MerstatHelp[] = {
-    "NAME",
-    "    mermin, mermax, merrange, mersum, mermean, meravg, merstd, merstd1, mervar, ",
-    "    mervar1, merskew, merkurt, mermedian, merpctl - Meridional statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "    merpctl,p  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes meridional statistical values of the input fields.",
-    "    Depending on the chosen operator, the meridional minimum, maximum, range, sum, average, standard deviation, variance,",
-    "    skewness, kurtosis, median or a certain percentile of the field is written to outfile.",
-    "    Operators of this module require all variables on the same regular lon/lat grid.",
-    "",
-    "OPERATORS",
-    "    mermin     Meridional minimum",
-    "               For every longitude the minimum over all latitudes is computed.",
-    "    mermax     Meridional maximum",
-    "               For every longitude the maximum over all latitudes is computed.",
-    "    merrange   Meridional range",
-    "               For every longitude the range over all latitudes is computed.",
-    "    mersum     Meridional sum",
-    "               For every longitude the sum over all latitudes is computed.",
-    "    mermean    Meridional mean",
-    "               For every longitude the area weighted mean over all latitudes is computed.",
-    "    meravg     Meridional average",
-    "               For every longitude the area weighted average over all latitudes is computed.",
-    "    merstd     Meridional standard deviation",
-    "               For every longitude the standard deviation over all latitudes is computed. Normalize by n.",
-    "    merstd1    Meridional standard deviation (n-1)",
-    "               For every longitude the standard deviation over all latitudes is computed. Normalize by (n-1).",
-    "    mervar     Meridional variance",
-    "               For every longitude the variance over all latitudes is computed. Normalize by n.",
-    "    mervar1    Meridional variance (n-1)",
-    "               For every longitude the variance over all latitudes is computed. Normalize by (n-1).",
-    "    merskew    Meridional skewness",
-    "               For every longitude the skewness over all latitudes is computed.",
-    "    merkurt    Meridional kurtosis",
-    "               For every longitude the kurtosis over all latitudes is computed.",
-    "    mermedian  Meridional median",
-    "               For every longitude the median over all latitudes is computed.",
-    "    merpctl    Meridional percentiles",
-    "               For every longitude the pth percentile over all latitudes is computed.",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    nullptr
-};
-
-static const char *GridboxstatHelp[] = {
-    "NAME",
-    "    gridboxmin, gridboxmax, gridboxrange, gridboxsum, gridboxmean, gridboxavg, ",
-    "    gridboxstd, gridboxstd1, gridboxvar, gridboxvar1, gridboxskew, gridboxkurt, ",
-    "    gridboxmedian - Statistical values over grid boxes",
-    "",
-    "SYNOPSIS",
-    "    <operator>,nx,ny  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over surrounding grid boxes.",
-    "    Depending on the chosen operator, the minimum, maximum, range, sum, average, standard deviation, variance,",
-    "    skewness, kurtosis or median of the neighboring grid boxes is written to outfile.",
-    "    All gridbox operators only work on quadrilateral curvilinear grids.",
-    "",
-    "OPERATORS",
-    "    gridboxmin     Gridbox minimum",
-    "                   Minimum value of the selected grid boxes.",
-    "    gridboxmax     Gridbox maximum",
-    "                   Maximum value of the selected grid boxes.",
-    "    gridboxrange   Gridbox range",
-    "                   Range (max-min value) of the selected grid boxes.",
-    "    gridboxsum     Gridbox sum",
-    "                   Sum of the selected grid boxes.",
-    "    gridboxmean    Gridbox mean",
-    "                   Mean of the selected grid boxes.",
-    "    gridboxavg     Gridbox average",
-    "                   Average of the selected grid boxes.",
-    "    gridboxstd     Gridbox standard deviation",
-    "                   Standard deviation of the selected grid boxes. Normalize by n.",
-    "    gridboxstd1    Gridbox standard deviation (n-1)",
-    "                   Standard deviation of the selected grid boxes. Normalize by (n-1).",
-    "    gridboxvar     Gridbox variance",
-    "                   Variance of the selected grid boxes. Normalize by n.",
-    "    gridboxvar1    Gridbox variance (n-1)",
-    "                   Variance of the selected grid boxes. Normalize by (n-1).",
-    "    gridboxskew    Gridbox skewness",
-    "                   Skewness of the selected grid boxes.",
-    "    gridboxkurt    Gridbox kurtosis",
-    "                   Kurtosis of the selected grid boxes.",
-    "    gridboxmedian  Gridbox median",
-    "                   Median of the selected grid boxes.",
-    "",
-    "PARAMETER",
-    "    nx  INTEGER  Number of grid boxes in x direction",
-    "    ny  INTEGER  Number of grid boxes in y direction",
-    nullptr
-};
-
-static const char *RemapstatHelp[] = {
-    "NAME",
-    "    remapmin, remapmax, remaprange, remapsum, remapmean, remapavg, remapstd, ",
-    "    remapstd1, remapvar, remapvar1, remapskew, remapkurt, remapmedian - ",
-    "    Remaps source points to target cells",
-    "",
-    "SYNOPSIS",
-    "    <operator>,grid  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module maps source points to target cells by calculating a statistical value from the source points.",
-    "    Each target cell contains the statistical value from all source points within that target cell.",
-    "    If there are no source points within a target cell, it gets a missing value.",
-    "    The target grid must be regular lon/lat or Gaussian.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance,",
-    "    standard deviation, skewness, kurtosis or median of source points is computed.",
-    "",
-    "OPERATORS",
-    "    remapmin     Remap minimum",
-    "                 Minimum value of the source points.",
-    "    remapmax     Remap maximum",
-    "                 Maximum value of the source points.",
-    "    remaprange   Remap range",
-    "                 Range (max-min value) of the source points.",
-    "    remapsum     Remap sum",
-    "                 Sum of the source points.",
-    "    remapmean    Remap mean",
-    "                 Mean of the source points.",
-    "    remapavg     Remap average",
-    "                 Average of the source points.",
-    "    remapstd     Remap standard deviation",
-    "                 Standard deviation of the source points. Normalize by n.",
-    "    remapstd1    Remap standard deviation (n-1)",
-    "                 Standard deviation of the source points. Normalize by (n-1).",
-    "    remapvar     Remap variance",
-    "                 Variance of the source points. Normalize by n.",
-    "    remapvar1    Remap variance (n-1)",
-    "                 Variance of the source points. Normalize by (n-1).",
-    "    remapskew    Remap skewness",
-    "                 Skewness of the source points.",
-    "    remapkurt    Remap kurtosis",
-    "                 Kurtosis of the source points.",
-    "    remapmedian  Remap median",
-    "                 Median of the source points.",
-    "",
-    "PARAMETER",
-    "    grid  STRING  Target grid description file or name",
-    nullptr
-};
-
-static const char *VertstatHelp[] = {
-    "NAME",
-    "    vertmin, vertmax, vertrange, vertsum, vertmean, vertavg, vertstd, vertstd1, ",
-    "    vertvar, vertvar1 - Vertical statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>,weights  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over all levels of the input variables.",
-    "    According to chosen operator the vertical minimum, maximum, range, sum, average, variance",
-    "    or standard deviation is written to outfile.",
-    "",
-    "OPERATORS",
-    "    vertmin    Vertical minimum",
-    "               For every gridpoint the minimum over all levels is computed.",
-    "    vertmax    Vertical maximum",
-    "               For every gridpoint the maximum over all levels is computed.",
-    "    vertrange  Vertical range",
-    "               For every gridpoint the range over all levels is computed.",
-    "    vertsum    Vertical sum",
-    "               For every gridpoint the sum over all levels is computed.",
-    "    vertmean   Vertical mean",
-    "               For every gridpoint the layer weighted mean over all levels is computed.",
-    "    vertavg    Vertical average",
-    "               For every gridpoint the layer weighted average over all levels is computed.",
-    "    vertstd    Vertical standard deviation",
-    "               For every gridpoint the standard deviation over all levels is computed. Normalize by n.",
-    "    vertstd1   Vertical standard deviation (n-1)",
-    "               For every gridpoint the standard deviation over all levels is computed. Normalize by (n-1).",
-    "    vertvar    Vertical variance",
-    "               For every gridpoint the variance over all levels is computed. Normalize by n.",
-    "    vertvar1   Vertical variance (n-1)",
-    "               For every gridpoint the variance over all levels is computed. Normalize by (n-1).",
-    "",
-    "PARAMETER",
-    "    weights  BOOL   weights=FALSE disables weighting by layer thickness [default: weights=TRUE]",
-    nullptr
-};
-
-static const char *TimselstatHelp[] = {
-    "NAME",
-    "    timselmin, timselmax, timselrange, timselsum, timselmean, timselavg, ",
-    "    timselstd, timselstd1, timselvar, timselvar1 - Time range statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>,nsets[,noffset[,nskip]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values for a selected number of timesteps. According to ",
-    "    the chosen operator the minimum, maximum, range, sum, average, variance or standard deviation of ",
-    "    the selected timesteps is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    timselmin    Time selection minimum",
-    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = min{i(t',x), t1 < t' <= tn}",
-    "    timselmax    Time selection maximum",
-    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = max{i(t',x), t1 < t' <= tn}",
-    "    timselrange  Time selection range",
-    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = range{i(t',x), t1 < t' <= tn}",
-    "    timselsum    Time selection sum",
-    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = sum{i(t',x), t1 < t' <= tn}",
-    "    timselmean   Time selection mean",
-    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = mean{i(t',x), t1 < t' <= tn}",
-    "    timselavg    Time selection average",
-    "                 For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = avg{i(t',x), t1 < t' <= tn}",
-    "    timselstd    Time selection standard deviation",
-    "                 Normalize by n. For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = std{i(t',x), t1 < t' <= tn}",
-    "    timselstd1   Time selection standard deviation (n-1)",
-    "                 Normalize by (n-1). For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = std1{i(t',x), t1 < t' <= tn}",
-    "    timselvar    Time selection variance",
-    "                 Normalize by n. For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = var{i(t',x), t1 < t' <= tn}",
-    "    timselvar1   Time selection variance (n-1)",
-    "                 Normalize by (n-1). For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "                 ",
-    "                 o(t,x) = var1{i(t',x), t1 < t' <= tn}",
-    "",
-    "PARAMETER",
-    "    nsets    INTEGER  Number of input timesteps for each output timestep ",
-    "    noffset  INTEGER  Number of input timesteps skipped before the first timestep range (optional)",
-    "    nskip    INTEGER  Number of input timesteps skipped between timestep ranges (optional)",
-    nullptr
-};
-
-static const char *TimselpctlHelp[] = {
-    "NAME",
-    "    timselpctl - Time range percentile values",
-    "",
-    "SYNOPSIS",
-    "    timselpctl,p,nsets[,noffset[,nskip]]  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentile values over a selected number of timesteps in infile1.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and infile3,",
-    "    respectively. The default number of histogram bins is 101. The default can be overridden by setting the",
-    "    environment variable CDO_PCTL_NBINS to a different value. The files infile2 and infile3 ",
-    "    should be the result of corresponding timselmin and timselmax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    For every adjacent sequence t1, ...., tn of timesteps of the same selected time range it is:",
-    "    ",
-    "    o(t,x) = pth percentile {i(t',x), t1 < t' <= tn}",
-    "",
-    "PARAMETER",
-    "    p        FLOAT    Percentile number in {0, ..., 100}",
-    "    nsets    INTEGER  Number of input timesteps for each output timestep ",
-    "    noffset  INTEGER  Number of input timesteps skipped before the first timestep range (optional)",
-    "    nskip    INTEGER  Number of input timesteps skipped between timestep ranges (optional)",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *RunstatHelp[] = {
-    "NAME",
-    "    runmin, runmax, runrange, runsum, runmean, runavg, runstd, runstd1, runvar, ",
-    "    runvar1 - Running statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>,nts  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes running statistical values over a selected number of timesteps. Depending on ",
-    "    the chosen operator the minimum, maximum, range, sum, average, variance or standard deviation of a selected ",
-    "    number of consecutive timesteps read from infile is written to outfile. ",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    runmin    Running minimum",
-    "              o(t+(nts-1)/2,x) = min{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runmax    Running maximum",
-    "              o(t+(nts-1)/2,x) = max{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runrange  Running range",
-    "              o(t+(nts-1)/2,x) = range{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runsum    Running sum",
-    "              o(t+(nts-1)/2,x) = sum{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runmean   Running mean",
-    "              o(t+(nts-1)/2,x) = mean{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runavg    Running average",
-    "              o(t+(nts-1)/2,x) = avg{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runstd    Running standard deviation",
-    "              Normalize by n. ",
-    "              ",
-    "              o(t+(nts-1)/2,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runstd1   Running standard deviation (n-1)",
-    "              Normalize by (n-1). ",
-    "              ",
-    "              o(t+(nts-1)/2,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runvar    Running variance",
-    "              Normalize by n. ",
-    "              ",
-    "              o(t+(nts-1)/2,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "    runvar1   Running variance (n-1)",
-    "              Normalize by (n-1). ",
-    "              ",
-    "              o(t+(nts-1)/2,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "",
-    "PARAMETER",
-    "    nts  INTEGER  Number of timesteps",
-    "",
-    "ENVIRONMENT",
-    "    CDO_TIMESTAT_DATE",
-    "        Sets the time stamp in outfile to the \"first\", \"middle\" or \"last\" contributing timestep of infile.",
-    nullptr
-};
-
-static const char *RunpctlHelp[] = {
-    "NAME",
-    "    runpctl - Running percentile values",
-    "",
-    "SYNOPSIS",
-    "    runpctl,p,nts  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes running percentiles over a selected number of timesteps in infile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    ",
-    "    o(t+(nts-1)/2,x) = pth percentile {i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
-    "",
-    "PARAMETER",
-    "    p    FLOAT    Percentile number in {0, ..., 100}",
-    "    nts  INTEGER  Number of timesteps",
-    nullptr
-};
-
-static const char *TimstatHelp[] = {
-    "NAME",
-    "    timmin, timmax, timrange, timsum, timmean, timavg, timstd, timstd1, timvar, ",
-    "    timvar1 - Statistical values over all timesteps",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over all timesteps in infile. Depending on ",
-    "    the chosen operator the minimum, maximum, range, sum, average, variance or standard deviation of ",
-    "    all timesteps read from infile is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    timmin    Time minimum",
-    "              o(1,x) = min{i(t',x), t_1<t'<=t_n}",
-    "    timmax    Time maximum",
-    "              o(1,x) = max{i(t',x), t_1<t'<=t_n}",
-    "    timrange  Time range",
-    "              o(1,x) = range{i(t',x), t_1<t'<=t_n}",
-    "    timsum    Time sum",
-    "              o(1,x) = sum{i(t',x), t_1<t'<=t_n}",
-    "    timmean   Time mean",
-    "              o(1,x) = mean{i(t',x), t_1<t'<=t_n}",
-    "    timavg    Time average",
-    "              o(1,x) = avg{i(t',x), t_1<t'<=t_n}",
-    "    timstd    Time standard deviation",
-    "              Normalize by n. ",
-    "              ",
-    "              o(1,x) = std{i(t',x), t_1<t'<=t_n}",
-    "    timstd1   Time standard deviation (n-1)",
-    "              Normalize by (n-1). ",
-    "              ",
-    "              o(1,x) = std1{i(t',x), t_1<t'<=t_n}",
-    "    timvar    Time variance",
-    "              Normalize by n. ",
-    "              ",
-    "              o(1,x) = var{i(t',x), t_1<t'<=t_n}",
-    "    timvar1   Time variance (n-1)",
-    "              Normalize by (n-1). ",
-    "              ",
-    "              o(1,x) = var1{i(t',x), t_1<t'<=t_n}",
-    nullptr
-};
-
-static const char *TimpctlHelp[] = {
-    "NAME",
-    "    timpctl - Percentile values over all timesteps",
-    "",
-    "SYNOPSIS",
-    "    timpctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentiles over all timesteps in infile1. The algorithm uses ",
-    "    histograms with minimum and maximum bounds given in infile2 and infile3, respectively. ",
-    "    The default number of histogram bins is 101. The default can be overridden by defining the",
-    "    environment variable CDO_PCTL_NBINS. The files infile2 and infile3 should be",
-    "    the result of corresponding timmin and timmax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    ",
-    "    o(1,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *HourstatHelp[] = {
-    "NAME",
-    "    hourmin, hourmax, hourrange, hoursum, hourmean, houravg, hourstd, hourstd1, ",
-    "    hourvar, hourvar1 - Hourly statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over timesteps of the same hour.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of timesteps of the same hour is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    hourmin    Hourly minimum",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = min{i(t',x), t_1<t'<=t_n}",
-    "    hourmax    Hourly maximum",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = max{i(t',x), t_1<t'<=t_n}",
-    "    hourrange  Hourly range",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = range{i(t',x), t_1<t'<=t_n}",
-    "    hoursum    Hourly sum",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
-    "    hourmean   Hourly mean",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
-    "    houravg    Hourly average",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
-    "    hourstd    Hourly standard deviation",
-    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = std{i(t',x), t_1<t'<=t_n}",
-    "    hourstd1   Hourly standard deviation (n-1)",
-    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = std1{i(t',x), t_1<t'<=t_n}",
-    "    hourvar    Hourly variance",
-    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = var{i(t',x), t_1<t'<=t_n}",
-    "    hourvar1   Hourly variance (n-1)",
-    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "               ",
-    "               o(t,x) = var1{i(t',x), t_1<t'<=t_n}",
-    nullptr
-};
-
-static const char *HourpctlHelp[] = {
-    "NAME",
-    "    hourpctl - Hourly percentile values",
-    "",
-    "SYNOPSIS",
-    "    hourpctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentiles over all timesteps of the same hour in infile1.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
-    "    infile3, respectively. The default number of histogram bins is 101.",
-    "    The default can be overridden by defining the environment variable CDO_PCTL_NBINS.",
-    "    The files infile2 and infile3 should be the result of corresponding hourmin",
-    "    and hourmax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same hour it is:",
-    "    ",
-    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *DaystatHelp[] = {
-    "NAME",
-    "    daymin, daymax, dayrange, daysum, daymean, dayavg, daystd, daystd1, dayvar, ",
-    "    dayvar1 - Daily statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over timesteps of the same day.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of timesteps of the same day is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    daymin    Daily minimum",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = min{i(t',x), t_1<t'<=t_n}",
-    "    daymax    Daily maximum",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = max{i(t',x), t_1<t'<=t_n}",
-    "    dayrange  Daily range",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = range{i(t',x), t_1<t'<=t_n}",
-    "    daysum    Daily sum",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
-    "    daymean   Daily mean",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
-    "    dayavg    Daily average",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
-    "    daystd    Daily standard deviation",
-    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = std{i(t',x), t_1<t'<=t_n}",
-    "    daystd1   Daily standard deviation (n-1)",
-    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = std1{i(t',x), t_1<t'<=t_n}",
-    "    dayvar    Daily variance",
-    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = var{i(t',x), t_1<t'<=t_n}",
-    "    dayvar1   Daily variance (n-1)",
-    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "              ",
-    "              o(t,x) = var1{i(t',x), t_1<t'<=t_n}",
-    nullptr
-};
-
-static const char *DaypctlHelp[] = {
-    "NAME",
-    "    daypctl - Daily percentile values",
-    "",
-    "SYNOPSIS",
-    "    daypctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentiles over all timesteps of the same day in infile1.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
-    "    infile3, respectively. The default number of histogram bins is 101.",
-    "    The default can be overridden by defining the environment variable CDO_PCTL_NBINS.",
-    "    The files infile2 and infile3 should be the result of corresponding daymin",
-    "    and daymax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same day it is:",
-    "    ",
-    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *MonstatHelp[] = {
-    "NAME",
-    "    monmin, monmax, monrange, monsum, monmean, monavg, monstd, monstd1, monvar, ",
-    "    monvar1 - Monthly statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over timesteps of the same month.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of timesteps of the same month is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    monmin    Monthly minimum",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = min{i(t',x), t_1<t'<=t_n}",
-    "    monmax    Monthly maximum",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = max{i(t',x), t_1<t'<=t_n}",
-    "    monrange  Monthly range",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = range{i(t',x), t_1<t'<=t_n}",
-    "    monsum    Monthly sum",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
-    "    monmean   Monthly mean",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
-    "    monavg    Monthly average",
-    "              For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
-    "    monstd    Monthly standard deviation",
-    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = std{i(t',x), t_1 < t' <= t_n}",
-    "    monstd1   Monthly standard deviation (n-1)",
-    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = std1{i(t',x), t_1 < t' <= t_n}",
-    "    monvar    Monthly variance",
-    "              Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = var{i(t',x), t_1 < t' <= t_n}",
-    "    monvar1   Monthly variance (n-1)",
-    "              Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "              ",
-    "              o(t,x) = var1{i(t',x), t_1 < t' <= t_n}",
-    nullptr
-};
-
-static const char *MonpctlHelp[] = {
-    "NAME",
-    "    monpctl - Monthly percentile values",
-    "",
-    "SYNOPSIS",
-    "    monpctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentiles over all timesteps of the same month in infile1.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
-    "    infile3, respectively. The default number of histogram bins is 101.",
-    "    The default can be overridden by defining the environment variable CDO_PCTL_NBINS.",
-    "    The files infile2 and infile3 should be the result of corresponding monmin",
-    "    and monmax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same month it is:",
-    "    ",
-    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *YearmonstatHelp[] = {
-    "NAME",
-    "    yearmonmean - Yearly mean from monthly data",
-    "",
-    "SYNOPSIS",
-    "    yearmonmean  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes the yearly mean of a monthly time series.",
-    "    Each month is weighted with the number of days per month. ",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    ",
-    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "    ",
-    "    o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_TIMESTAT_DATE",
-    "        Sets the date information in outfile to the \"first\", \"middle\" or \"last\" contributing timestep of infile.",
-    nullptr
-};
-
-static const char *YearstatHelp[] = {
-    "NAME",
-    "    yearmin, yearmax, yearminidx, yearmaxidx, yearrange, yearsum, yearmean, ",
-    "    yearavg, yearstd, yearstd1, yearvar, yearvar1 - Yearly statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over timesteps of the same year.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of timesteps of the same year is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    yearmin     Yearly minimum",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = min{i(t',x), t_1<t'<=t_n}",
-    "    yearmax     Yearly maximum",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = max{i(t',x), t_1<t'<=t_n}",
-    "    yearminidx  Yearly minimum indices",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = minidx{i(t',x), t_1<t'<=t_n}",
-    "    yearmaxidx  Yearly maximum indices",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = maxidx{i(t',x), t_1<t'<=t_n}",
-    "    yearrange   Yearly range",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = range{i(t',x), t_1<t'<=t_n}",
-    "    yearsum     Yearly sum",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = sum{i(t',x), t_1<t'<=t_n}",
-    "    yearmean    Yearly mean",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = mean{i(t',x), t_1<t'<=t_n}",
-    "    yearavg     Yearly average",
-    "                For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = avg{i(t',x), t_1<t'<=t_n}",
-    "    yearstd     Yearly standard deviation",
-    "                Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = std{i(t',x), t_1 < t' <= t_n}",
-    "    yearstd1    Yearly standard deviation (n-1)",
-    "                Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = std1{i(t',x), t_1 < t' <= t_n}",
-    "    yearvar     Yearly variance",
-    "                Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = var{i(t',x), t_1 < t' <= t_n}",
-    "    yearvar1    Yearly variance (n-1)",
-    "                Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "                ",
-    "                o(t,x) = var1{i(t',x), t_1 < t' <= t_n}",
-    "",
-    "NOTE",
-    "    The operators yearmean and yearavg compute only arithmetical means!",
-    nullptr
-};
-
-static const char *YearpctlHelp[] = {
-    "NAME",
-    "    yearpctl - Yearly percentile values",
-    "",
-    "SYNOPSIS",
-    "    yearpctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentiles over all timesteps of the same year in infile1.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
-    "    infile3, respectively. The default number of histogram bins is 101. The default can be",
-    "    overridden by defining the environment variable CDO_PCTL_NBINS. The files infile2 and",
-    "    infile3 should be the result of corresponding yearmin and yearmax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same year it is:",
-    "    ",
-    "    o(t,x) = pth percentile {i(t',x), t_1<t'<=t_n}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *SeasstatHelp[] = {
-    "NAME",
-    "    seasmin, seasmax, seasrange, seassum, seasmean, seasavg, seasstd, seasstd1, ",
-    "    seasvar, seasvar1 - Seasonal statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values over timesteps of the same season.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of timesteps of the same season is written to outfile.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    Be careful about the first and the last output timestep, they may be incorrect values ",
-    "    if the seasons have incomplete timesteps.",
-    "",
-    "OPERATORS",
-    "    seasmin    Seasonal minimum",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = min{i(t',x), t1 < t' <= tn}",
-    "    seasmax    Seasonal maximum",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = max{i(t',x), t1 < t' <= tn}",
-    "    seasrange  Seasonal range",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = range{i(t',x), t1 < t' <= tn}",
-    "    seassum    Seasonal sum",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = sum{i(t',x), t1 < t' <= tn}",
-    "    seasmean   Seasonal mean",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = mean{i(t',x), t1 < t' <= tn}",
-    "    seasavg    Seasonal average",
-    "               For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = avg{i(t',x), t1 < t' <= tn}",
-    "    seasstd    Seasonal standard deviation",
-    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = std{i(t',x), t1 < t' <= tn}",
-    "    seasstd1   Seasonal standard deviation (n-1)",
-    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = std1{i(t',x), t1 < t' <= tn}",
-    "    seasvar    Seasonal variance",
-    "               Normalize by n. For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = var{i(t',x), t1 < t' <= tn}",
-    "    seasvar1   Seasonal variance (n-1)",
-    "               Normalize by (n-1). For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "               ",
-    "               o(t,x) = var1{i(t',x), t1 < t' <= tn}",
-    nullptr
-};
-
-static const char *SeaspctlHelp[] = {
-    "NAME",
-    "    seaspctl - Seasonal percentile values",
-    "",
-    "SYNOPSIS",
-    "    seaspctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes percentiles over all timesteps in infile1 of the same season.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and infile3,",
-    "    respectively. The default number of histogram bins is 101. The default can be overridden",
-    "    by defining the environment variable CDO_PCTL_NBINS. The files infile2 and infile3",
-    "    should be the result of corresponding seasmin and seasmax operations, respectively.",
-    "    The time of outfile is determined by the time in the middle of all contributing timesteps of infile1.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "    Be careful about the first and the last output timestep, they may be incorrect values ",
-    "    if the seasons have incomplete timesteps.",
-    "    For every adjacent sequence t_1, ...,t_n of timesteps of the same season it is:",
-    "    ",
-    "    o(t,x) = pth percentile {i(t',x), t1 < t' <= tn}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *YhourstatHelp[] = {
-    "NAME",
-    "    yhourmin, yhourmax, yhourrange, yhoursum, yhourmean, yhouravg, yhourstd, ",
-    "    yhourstd1, yhourvar, yhourvar1 - Multi-year hourly statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values of each hour and day of year.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of each hour and day of year in infile is written to outfile.",
-    "    The date information in an output field is the date of the last contributing input field.",
-    "",
-    "OPERATORS",
-    "    yhourmin    Multi-year hourly minimum",
-    "                o(0001,x) = min{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = min{i(t,x), day(i(t)) = 8784}",
-    "    yhourmax    Multi-year hourly maximum",
-    "                o(0001,x) = max{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = max{i(t,x), day(i(t)) = 8784}",
-    "    yhourrange  Multi-year hourly range",
-    "                o(0001,x) = range{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = range{i(t,x), day(i(t)) = 8784}",
-    "    yhoursum    Multi-year hourly sum",
-    "                o(0001,x) = sum{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = sum{i(t,x), day(i(t)) = 8784}",
-    "    yhourmean   Multi-year hourly mean",
-    "                o(0001,x) = mean{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = mean{i(t,x), day(i(t)) = 8784}",
-    "    yhouravg    Multi-year hourly average",
-    "                o(0001,x) = avg{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = avg{i(t,x), day(i(t)) = 8784}",
-    "    yhourstd    Multi-year hourly standard deviation",
-    "                Normalize by n. ",
-    "                ",
-    "                o(0001,x) = std{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = std{i(t,x), day(i(t)) = 8784}",
-    "    yhourstd1   Multi-year hourly standard deviation (n-1)",
-    "                Normalize by (n-1). ",
-    "                ",
-    "                o(0001,x) = std1{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = std1{i(t,x), day(i(t)) = 8784}",
-    "    yhourvar    Multi-year hourly variance",
-    "                Normalize by n. ",
-    "                ",
-    "                o(0001,x) = var{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = var{i(t,x), day(i(t)) = 8784}",
-    "    yhourvar1   Multi-year hourly variance (n-1)",
-    "                Normalize by (n-1). ",
-    "                ",
-    "                o(0001,x) = var1{i(t,x), day(i(t)) = 0001}",
-    "                                 ...",
-    "                o(8784,x) = var1{i(t,x), day(i(t)) = 8784}",
-    nullptr
-};
-
-static const char *DhourstatHelp[] = {
-    "NAME",
-    "    dhourmin, dhourmax, dhourrange, dhoursum, dhourmean, dhouravg, dhourstd, ",
-    "    dhourstd1, dhourvar, dhourvar1 - Multi-day hourly statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values of each hour of day.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of each hour of day in infile is written to outfile.",
-    "    The date information in an output field is the date of the last contributing input field.",
-    "",
-    "OPERATORS",
-    "    dhourmin    Multi-day hourly minimum",
-    "                o(01,x) = min{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = min{i(t,x), day(i(t)) = 24}",
-    "    dhourmax    Multi-day hourly maximum",
-    "                o(01,x) = max{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = max{i(t,x), day(i(t)) = 24}",
-    "    dhourrange  Multi-day hourly range",
-    "                o(01,x) = range{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = range{i(t,x), day(i(t)) = 24}",
-    "    dhoursum    Multi-day hourly sum",
-    "                o(01,x) = sum{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = sum{i(t,x), day(i(t)) = 24}",
-    "    dhourmean   Multi-day hourly mean",
-    "                o(01,x) = mean{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = mean{i(t,x), day(i(t)) = 24}",
-    "    dhouravg    Multi-day hourly average",
-    "                o(01,x) = avg{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = avg{i(t,x), day(i(t)) = 24}",
-    "    dhourstd    Multi-day hourly standard deviation",
-    "                Normalize by n. ",
-    "                ",
-    "                o(01,x) = std{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = std{i(t,x), day(i(t)) = 24}",
-    "    dhourstd1   Multi-day hourly standard deviation (n-1)",
-    "                Normalize by (n-1). ",
-    "                ",
-    "                o(01,x) = std1{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = std1{i(t,x), day(i(t)) = 24}",
-    "    dhourvar    Multi-day hourly variance",
-    "                Normalize by n. ",
-    "                ",
-    "                o(01,x) = var{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = var{i(t,x), day(i(t)) = 24}",
-    "    dhourvar1   Multi-day hourly variance (n-1)",
-    "                Normalize by (n-1). ",
-    "                ",
-    "                o(01,x) = var1{i(t,x), day(i(t)) = 01}",
-    "                                 ...",
-    "                o(24,x) = var1{i(t,x), day(i(t)) = 24}",
-    nullptr
-};
-
-static const char *YdaystatHelp[] = {
-    "NAME",
-    "    ydaymin, ydaymax, ydayrange, ydaysum, ydaymean, ydayavg, ydaystd, ydaystd1, ",
-    "    ydayvar, ydayvar1 - Multi-year daily statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values of each day of year.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of each day of year in infile is written to outfile.",
-    "    The date information in an output field is the date of the last contributing input field.",
-    "",
-    "OPERATORS",
-    "    ydaymin    Multi-year daily minimum",
-    "               o(001,x) = min{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = min{i(t,x), day(i(t)) = 366}",
-    "    ydaymax    Multi-year daily maximum",
-    "               o(001,x) = max{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = max{i(t,x), day(i(t)) = 366}",
-    "    ydayrange  Multi-year daily range",
-    "               o(001,x) = range{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = range{i(t,x), day(i(t)) = 366}",
-    "    ydaysum    Multi-year daily sum",
-    "               o(001,x) = sum{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = sum{i(t,x), day(i(t)) = 366}",
-    "    ydaymean   Multi-year daily mean",
-    "               o(001,x) = mean{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = mean{i(t,x), day(i(t)) = 366}",
-    "    ydayavg    Multi-year daily average",
-    "               o(001,x) = avg{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = avg{i(t,x), day(i(t)) = 366}",
-    "    ydaystd    Multi-year daily standard deviation",
-    "               Normalize by n. ",
-    "               ",
-    "               o(001,x) = std{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = std{i(t,x), day(i(t)) = 366}",
-    "    ydaystd1   Multi-year daily standard deviation (n-1)",
-    "               Normalize by (n-1). ",
-    "               ",
-    "               o(001,x) = std1{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = std1{i(t,x), day(i(t)) = 366}",
-    "    ydayvar    Multi-year daily variance",
-    "               Normalize by n. ",
-    "               ",
-    "               o(001,x) = var{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = var{i(t,x), day(i(t)) = 366}",
-    "    ydayvar1   Multi-year daily variance (n-1)",
-    "               Normalize by (n-1). ",
-    "               ",
-    "               o(001,x) = var1{i(t,x), day(i(t)) = 001}",
-    "                                ...",
-    "               o(366,x) = var1{i(t,x), day(i(t)) = 366}",
-    nullptr
-};
-
-static const char *YdaypctlHelp[] = {
-    "NAME",
-    "    ydaypctl - Multi-year daily percentile values",
-    "",
-    "SYNOPSIS",
-    "    ydaypctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator writes a certain percentile of each day of year in infile1 to outfile.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and",
-    "    infile3, respectively. The default number of histogram bins is 101. The default can be",
-    "    overridden by setting the environment variable CDO_PCTL_NBINS to a different value.",
-    "    The files infile2 and infile3 should be the result of corresponding ydaymin and",
-    "    ydaymax operations, respectively.",
-    "    The date information in an output field is the date of the last contributing input field.",
-    "    ",
-    "    o(001,x) = pth percentile {i(t,x), day(i(t)) = 001}",
-    "                     ...",
-    "    o(366,x) = pth percentile {i(t,x), day(i(t)) = 366}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *YmonstatHelp[] = {
-    "NAME",
-    "    ymonmin, ymonmax, ymonrange, ymonsum, ymonmean, ymonavg, ymonstd, ymonstd1, ",
-    "    ymonvar, ymonvar1 - Multi-year monthly statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values of each month of year.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of each month of year in infile is written to outfile.",
-    "    The date information in an output field is the date of the last contributing input field.",
-    "    This can be change with the CDO option --timestat_date <first|middle|last>.",
-    "",
-    "OPERATORS",
-    "    ymonmin    Multi-year monthly minimum",
-    "               o(01,x) = min{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = min{i(t,x), month(i(t)) = 12}",
-    "    ymonmax    Multi-year monthly maximum",
-    "               o(01,x) = max{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = max{i(t,x), month(i(t)) = 12}",
-    "    ymonrange  Multi-year monthly range",
-    "               o(01,x) = range{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = range{i(t,x), month(i(t)) = 12}",
-    "    ymonsum    Multi-year monthly sum",
-    "               o(01,x) = sum{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = sum{i(t,x), month(i(t)) = 12}",
-    "    ymonmean   Multi-year monthly mean",
-    "               o(01,x) = mean{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = mean{i(t,x), month(i(t)) = 12}",
-    "    ymonavg    Multi-year monthly average",
-    "               o(01,x) = avg{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = avg{i(t,x), month(i(t)) = 12}",
-    "    ymonstd    Multi-year monthly standard deviation",
-    "               Normalize by n. ",
-    "               ",
-    "               o(01,x) = std{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = std{i(t,x), month(i(t)) = 12}",
-    "    ymonstd1   Multi-year monthly standard deviation (n-1)",
-    "               Normalize by (n-1). ",
-    "               ",
-    "               o(01,x) = std1{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = std1{i(t,x), month(i(t)) = 12}",
-    "    ymonvar    Multi-year monthly variance",
-    "               Normalize by n. ",
-    "               ",
-    "               o(01,x) = var{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = var{i(t,x), month(i(t)) = 12}",
-    "    ymonvar1   Multi-year monthly variance (n-1)",
-    "               Normalize by (n-1). ",
-    "               ",
-    "               o(01,x) = var1{i(t,x), month(i(t)) = 01}",
-    "                                ...",
-    "               o(12,x) = var1{i(t,x), month(i(t)) = 12}",
-    nullptr
-};
-
-static const char *YmonpctlHelp[] = {
-    "NAME",
-    "    ymonpctl - Multi-year monthly percentile values",
-    "",
-    "SYNOPSIS",
-    "    ymonpctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator writes a certain percentile of each month of year in infile1 to outfile.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in",
-    "    infile2 and infile3, respectively. The default number of",
-    "    histogram bins is 101. The default can be overridden by setting the",
-    "    environment variable CDO_PCTL_NBINS to a different value. The files",
-    "    infile2 and infile3 should be the result of corresponding",
-    "    ymonmin and ymonmax operations, respectively.",
-    "    The date information in an output field is the date of the last",
-    "    contributing input field.",
-    "    ",
-    "    o(01,x) = pth percentile {i(t,x), month(i(t)) = 01}",
-    "                     ...",
-    "    o(12,x) = pth percentile {i(t,x), month(i(t)) = 12}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *YseasstatHelp[] = {
-    "NAME",
-    "    yseasmin, yseasmax, yseasrange, yseassum, yseasmean, yseasavg, yseasstd, ",
-    "    yseasstd1, yseasvar, yseasvar1 - Multi-year seasonal statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module computes statistical values of each season.",
-    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
-    "    or standard deviation of each season in infile is written to outfile.",
-    "    The date information in an output field is the date of the last contributing input field.",
-    "",
-    "OPERATORS",
-    "    yseasmin    Multi-year seasonal minimum",
-    "                o(1,x) = min{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = min{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = min{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = min{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasmax    Multi-year seasonal maximum",
-    "                o(1,x) = max{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = max{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = max{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = max{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasrange  Multi-year seasonal range",
-    "                o(1,x) = range{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = range{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = range{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = range{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseassum    Multi-year seasonal sum",
-    "                o(1,x) = sum{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = sum{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = sum{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = sum{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasmean   Multi-year seasonal mean",
-    "                o(1,x) = mean{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = mean{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = mean{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = mean{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasavg    Multi-year seasonal average",
-    "                o(1,x) = avg{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = avg{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = avg{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = avg{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasstd    Multi-year seasonal standard deviation",
-    "                o(1,x) = std{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = std{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = std{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = std{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasstd1   Multi-year seasonal standard deviation (n-1)",
-    "                o(1,x) = std1{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = std1{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = std1{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = std1{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasvar    Multi-year seasonal variance",
-    "                o(1,x) = var{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = var{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = var{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = var{i(t,x), month(i(t)) = 09, 10, 11}",
-    "    yseasvar1   Multi-year seasonal variance (n-1)",
-    "                o(1,x) = var1{i(t,x), month(i(t)) = 12, 01, 02}",
-    "                o(2,x) = var1{i(t,x), month(i(t)) = 03, 04, 05}",
-    "                o(3,x) = var1{i(t,x), month(i(t)) = 06, 07, 08}",
-    "                o(4,x) = var1{i(t,x), month(i(t)) = 09, 10, 11}",
-    nullptr
-};
-
-static const char *YseaspctlHelp[] = {
-    "NAME",
-    "    yseaspctl - Multi-year seasonal percentile values",
-    "",
-    "SYNOPSIS",
-    "    yseaspctl,p  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator writes a certain percentile of each season in infile1 to outfile.",
-    "    The algorithm uses histograms with minimum and maximum bounds given in",
-    "    infile2 and infile3, respectively. The default number of",
-    "    histogram bins is 101. The default can be overridden by setting the",
-    "    environment variable CDO_PCTL_NBINS to a different value. The files",
-    "    infile2 and infile3 should be the result of corresponding",
-    "    yseasmin and yseasmax operations, respectively.",
-    "    The date information in an output field is the date of the last",
-    "    contributing input field.",
-    "    ",
-    "    o(1,x) = pth percentile {i(t,x), month(i(t)) = 12, 01, 02}",
-    "    o(2,x) = pth percentile {i(t,x), month(i(t)) = 03, 04, 05}",
-    "    o(3,x) = pth percentile {i(t,x), month(i(t)) = 06, 07, 08}",
-    "    o(4,x) = pth percentile {i(t,x), month(i(t)) = 09, 10, 11}",
-    "",
-    "PARAMETER",
-    "    p  FLOAT  Percentile number in {0, ..., 100}",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *YdrunstatHelp[] = {
-    "NAME",
-    "    ydrunmin, ydrunmax, ydrunsum, ydrunmean, ydrunavg, ydrunstd, ydrunstd1, ",
-    "    ydrunvar, ydrunvar1 - Multi-year daily running statistical values",
-    "",
-    "SYNOPSIS",
-    "    <operator>,nts  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module writes running statistical values for each day of year in infile to outfile.",
-    "    Depending on the chosen operator, the minimum, maximum, sum, average, variance or standard deviation ",
-    "    of all timesteps in running windows of which the medium timestep corresponds to a certain day of",
-    "    year is computed. The date information in an output field is the date of the timestep in the middle ",
-    "    of the last contributing running window.",
-    "    Note that the operator have to be applied to a continuous time series of daily measurements in order ",
-    "    to yield physically meaningful results. Also note that the output time series begins (nts-1)/2 timesteps",
-    "    after the first timestep of the input time series and ends (nts-1)/2 timesteps before the last one.",
-    "    For input data which are complete but not continuous, such as time series of daily measurements for ",
-    "    the same month or season within different years, the operator yields physically meaningful results ",
-    "    only if the input time series does include the (nts-1)/2 days before and after each period of interest.",
-    "",
-    "OPERATORS",
-    "    ydrunmin   Multi-year daily running minimum",
-    "               o(001,x) = min{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = min{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "    ydrunmax   Multi-year daily running maximum",
-    "               o(001,x) = max{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = max{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "    ydrunsum   Multi-year daily running sum",
-    "               o(001,x) = sum{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = sum{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "    ydrunmean  Multi-year daily running mean",
-    "               o(001,x) = mean{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = mean{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "    ydrunavg   Multi-year daily running average",
-    "               o(001,x) = avg{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = avg{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "    ydrunstd   Multi-year daily running standard deviation",
-    "               Normalize by n. ",
-    "               ",
-    "               o(001,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 366}",
-    "    ydrunstd1  Multi-year daily running standard deviation (n-1)",
-    "               Normalize by (n-1). ",
-    "               ",
-    "               o(001,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 366}",
-    "    ydrunvar   Multi-year daily running variance",
-    "               Normalize by n. ",
-    "               ",
-    "               o(001,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "    ydrunvar1  Multi-year daily running variance (n-1)",
-    "               Normalize by (n-1). ",
-    "               ",
-    "               o(001,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                                ...",
-    "               o(366,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "",
-    "PARAMETER",
-    "    nts  INTEGER  Number of timesteps",
-    nullptr
-};
-
-static const char *YdrunpctlHelp[] = {
-    "NAME",
-    "    ydrunpctl - Multi-year daily running percentile values",
-    "",
-    "SYNOPSIS",
-    "    ydrunpctl,p,nts  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator writes running percentile values for each day of year in infile1 to outfile. ",
-    "    A certain percentile is computed for all timesteps in running windows of which the medium ",
-    "    timestep corresponds to a certain day of year. ",
-    "    The algorithm uses histograms with minimum and maximum bounds given in infile2 and infile3,",
-    "    respectively. The default number of histogram bins is 101. The default can be overridden",
-    "    by setting the environment variable CDO_PCTL_NBINS to a different value. The files infile2 ",
-    "    and infile3 should be the result of corresponding ydrunmin and ydrunmax operations, respectively.",
-    "    The date information in an output field is the date of the timestep in the middle of the last contributing running window.",
-    "    Note that the operator have to be applied to a continuous time series of daily measurements ",
-    "    in order to yield physically meaningful results. Also note that the output time series begins",
-    "    (nts-1)/2 timesteps after the first timestep of the input time series and ends (nts-1)/2 ",
-    "    timesteps before the last.",
-    "    For input data which are complete but not continuous, such as time series of daily measurements ",
-    "    for the same month or season within different years, the operator only yields physically meaningful ",
-    "    results if the input time series does include the (nts-1)/2 days before and after each period ",
-    "    of interest.",
-    "    ",
-    "    o(001,x) = pth percentile {i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}",
-    "                     ...",
-    "    o(366,x) = pth percentile {i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 366}",
-    "",
-    "PARAMETER",
-    "    p    FLOAT    Percentile number in {0, ..., 100}",
-    "    nts  INTEGER  Number of timesteps",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-
-static const char *FldcorHelp[] = {
-    "NAME",
-    "    fldcor - Correlation in grid space",
-    "",
-    "SYNOPSIS",
-    "    fldcor  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    The correlation coefficient is a quantity that gives the quality of a least ",
-    "    squares fitting to the original data. This operator correlates all gridpoints",
-    "    of two fields for each timestep. With",
-    "    ",
-    "    S(t) = {x, i_1(t,x) != missval and i_2(t,x) != missval}",
-    "    ",
-    "    it is",
-    "    ",
-    "    o(t,1) = Cor{(i_1(t,x), i_2(t,x)), x_1 < x <= x_n}",
-    "    ",
-    "    where w(x) are the area weights obtained by the input streams.",
-    "    For every timestep t only those field elements x belong to the sample,",
-    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
-    nullptr
-};
-
-static const char *TimcorHelp[] = {
-    "NAME",
-    "    timcor - Correlation over time",
-    "",
-    "SYNOPSIS",
-    "    timcor  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    The correlation coefficient is a quantity that gives the quality of a least ",
-    "    squares fitting to the original data. This operator correlates each gridpoint",
-    "    of two fields over all timesteps. With",
-    "    ",
-    "    S(x) = {t, i_1(t,x) != missval and i_2(t,x) != missval}",
-    "    ",
-    "    it is",
-    "    ",
-    "    o(1,x) = Cor{(i_1(t,x), i_2(t,x)), t_1 < t <= t_n}",
-    "    ",
-    "    For every gridpoint x only those timesteps t belong to the sample,",
-    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
-    nullptr
-};
-
-static const char *FldcovarHelp[] = {
-    "NAME",
-    "    fldcovar - Covariance in grid space",
-    "",
-    "SYNOPSIS",
-    "    fldcovar  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator calculates the covariance of two fields over all gridpoints",
-    "    for each timestep. With",
-    "    ",
-    "    S(t) = {x, i_1(t,x) != missval and i_2(t,x) != missval}",
-    "    ",
-    "    it is",
-    "    ",
-    "    o(t,1) = Covar{(i_1(t,x), i_2(t,x)), x_1 < x <= x_n}",
-    "    ",
-    "    where w(x) are the area weights obtained by the input streams.",
-    "    For every timestep t only those field elements x belong to the sample,",
-    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
-    nullptr
-};
-
-static const char *TimcovarHelp[] = {
-    "NAME",
-    "    timcovar - Covariance over time",
-    "",
-    "SYNOPSIS",
-    "    timcovar  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator calculates the covariance of two fields at each gridpoint",
-    "    over all timesteps. With",
-    "    ",
-    "    S(x) = {t, i_1(t,x) != missval and i_2(t,x) != missval}",
-    "    ",
-    "    it is",
-    "    ",
-    "    o(1,x) = Covar{(i_1(t,x), i_2(t,x)), t_1 < t <= t_n}",
-    "    ",
-    "    For every gridpoint x only those timesteps t belong to the sample,",
-    "    which have i_1(t,x) != missval and i_2(t,x) != missval.",
-    nullptr
-};
-
-static const char *RegresHelp[] = {
-    "NAME",
-    "    regres - Regression",
-    "",
-    "SYNOPSIS",
-    "    regres[,equal]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    The values of the input file infile are assumed to be distributed as",
-    "    N(a+b*t,S^2) with unknown a, b and S^2. This operator estimates the",
-    "    parameter b. For every field element x only those timesteps ",
-    "    t belong to the sample S(x), which have i(t,x) NE miss.",
-    "    It is assumed that all timesteps are equidistant, if this is not the case set the parameter equal=false.",
-    "",
-    "PARAMETER",
-    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
-    nullptr
-};
-
-static const char *DetrendHelp[] = {
-    "NAME",
-    "    detrend - Detrend time series",
-    "",
-    "SYNOPSIS",
-    "    detrend[,equal]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Every time series in infile is linearly detrended. For every field element x ",
-    "    only those timesteps t belong to the sample S(x), which have i(t,x) NE miss.",
-    "    It is assumed that all timesteps are equidistant, if this is not the case set the parameter equal=false.",
-    "",
-    "PARAMETER",
-    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
-    "",
-    "NOTE",
-    "    This operator has to keep the fields of all timesteps concurrently in the memory.",
-    "    If not enough memory is available use the operators trend and subtrend.",
-    nullptr
-};
-
-static const char *TrendHelp[] = {
-    "NAME",
-    "    trend - Trend of time series",
-    "",
-    "SYNOPSIS",
-    "    trend[,equal]  infile outfile1 outfile2",
-    "",
-    "DESCRIPTION",
-    "    The values of the input file infile are assumed to be distributed as",
-    "    N(a+b*t,S^2) with unknown a, b and S^2. This operator estimates the",
-    "    parameter a and b. For every field element x only those timesteps ",
-    "    t belong to the sample S(x), which have i(t,x) NE miss.",
-    "    Thus the estimation for a is stored in outfile1 and that for b is stored ",
-    "    in outfile2. To subtract the trend from the data see operator subtrend.",
-    "    It is assumed that all timesteps are equidistant, if this is not the case set the parameter equal=false.",
-    "",
-    "PARAMETER",
-    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
-    nullptr
-};
-
-static const char *TrendarithHelp[] = {
-    "NAME",
-    "    addtrend, subtrend - Add or subtract a trend",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,equal]  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module is for adding or subtracting a trend computed by the operator trend.",
-    "",
-    "OPERATORS",
-    "    addtrend  Add trend",
-    "              It is",
-    "              ",
-    "              o(t,x) = i_1(t,x) + (i_2(1,x) + i_3(1,x)*t)",
-    "              where t is the timesteps.",
-    "    subtrend  Subtract trend",
-    "              It is",
-    "              ",
-    "              o(t,x) = i_1(t,x) - (i_2(1,x) + i_3(1,x)*t)",
-    "              where t is the timesteps.",
-    "",
-    "PARAMETER",
-    "    equal  BOOL  Set to false for unequal distributed timesteps (default: true)",
-    nullptr
-};
-
-static const char *EOFsHelp[] = {
-    "NAME",
-    "    eof, eoftime, eofspatial, eof3d - Empirical Orthogonal Functions",
-    "",
-    "SYNOPSIS",
-    "    <operator>,neof  infile outfile1 outfile2",
-    "",
-    "DESCRIPTION",
-    "    This module calculates empirical orthogonal functions of the data in infile ",
-    "    as the eigen values of the scatter matrix (covariance matrix) S of the data",
-    "    sample z(t). A more detailed description can be found above.",
-    "    ",
-    "    Please note, that the input data are assumed to be anomalies.",
-    "    ",
-    "    If operator eof is chosen, the EOFs are computed in either time or spatial",
-    "    space, whichever is the fastest. If the user already knows, which computation",
-    "    is faster, the module can be forced to perform a computation in time- or gridspace",
-    "    by using the operators eoftime or eofspatial, respectively. This can enhance ",
-    "    performance, especially for very long time series, where the number of timesteps",
-    "    is larger than the number of grid-points. Data in infile are assumed to be anomalies.",
-    "    If they are not, the behavior of this module is not well defined. ",
-    "    After execution outfile1 will contain all eigen-values and outfile2 the",
-    "    eigenvectors e_j. All EOFs and eigen-values are computed. However, only the first ",
-    "    neof EOFs are written to outfile2. Nonetheless, outfile1 contains all eigen-values. ",
-    "    ",
-    "    Missing values are not fully supported. Support is only checked for non-changing",
-    "    masks of missing values in time. Although there still will be results, they are",
-    "    not trustworthy, and a warning will occur. In the latter case we suggest to ",
-    "    replace missing values by 0 in infile. ",
-    "",
-    "OPERATORS",
-    "    eof         Calculate EOFs in spatial or time space",
-    "    eoftime     Calculate EOFs in time space",
-    "    eofspatial  Calculate EOFs in spatial space",
-    "    eof3d       Calculate 3-Dimensional EOFs in time space",
-    "",
-    "PARAMETER",
-    "    neof  INTEGER  Number of eigen functions",
-    "",
-    "ENVIRONMENT",
-    "    CDO_SVD_MODE   ",
-    "        Is used to choose the algorithm for eigenvalue calculation. Options are 'jacobi' for ",
-    "        a one-sided parallel jacobi-algorithm (only executed in parallel if -P flag is set)",
-    "        and  'danielson_lanczos' for a non-parallel d/l algorithm. The default setting is 'jacobi'.",
-    "    CDO_WEIGHT_MODE",
-    "        It is used to set the weight mode. The default is 'off'. Set it to 'on' for a weighted version.",
-    "    MAX_JACOBI_ITER",
-    "        Is the maximum integer number of annihilation sweeps that is executed if the ",
-    "        jacobi-algorithm is used to compute the eigen values. The default value is 12.",
-    "    FNORM_PRECISION",
-    "        Is the Frobenius norm of the matrix consisting of an annihilation pair",
-    "        of eigenvectors that is used to determine if the eigenvectors have reached ",
-    "        a sufficient level of convergence. If all annihilation-pairs of vectors have ",
-    "        a norm below this value, the computation is considered to have converged ",
-    "        properly. Otherwise, a warning will occur. The default value 1e-12.",
-    nullptr
-};
-
-static const char *EofcoeffHelp[] = {
-    "NAME",
-    "    eofcoeff - Principal coefficients of EOFs",
-    "",
-    "SYNOPSIS",
-    "    eofcoeff  infile1 infile2 obase",
-    "",
-    "DESCRIPTION",
-    "    This module calculates the time series of the principal coefficients for given EOF",
-    "    (empirical orthogonal functions) and data. Time steps in infile1 are assumed to be the EOFs,",
-    "    time steps in infile2 are assumed to be the time series.",
-    "    Note, that this operator calculates a non weighted dot product of the fields in infile1 and infile2.",
-    "    For consistency set the environment variable CDO_WEIGHT_MODE=off when using eof or eof3d.",
-    "    ",
-    "    There will be a separate file containing a time series of principal coefficients",
-    "    with time information from infile2 for each EOF in infile1. Output files will be",
-    "    numbered as <obase><neof><suffix> where neof+1 is the number of the EOF (timestep)",
-    "    in infile1 and suffix is the filename extension derived from the file format. ",
-    "",
-    "ENVIRONMENT",
-    "    CDO_FILE_SUFFIX",
-    "        Set the default file suffix. This suffix will be added to the output file ",
-    "        names instead of the filename extension derived from the file format. ",
-    "        Set this variable to NULL to disable the adding of a file suffix.",
-    nullptr
-};
-
-static const char *RemapbilHelp[] = {
-    "NAME",
-    "    remapbil, genbil - Bilinear interpolation",
-    "",
-    "SYNOPSIS",
-    "    remapbil,grid  infile outfile",
-    "    genbil,grid[,map3d]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators for a bilinear remapping of fields between grids in spherical coordinates.",
-    "    The interpolation is based on an adapted SCRIP library version. ",
-    "    For a detailed description of the interpolation method see SCRIP.",
-    "    This interpolation method only works on quadrilateral curvilinear source grids.",
-    "",
-    "OPERATORS",
-    "    remapbil  Bilinear interpolation",
-    "              Performs a bilinear interpolation on all input fields.",
-    "    genbil    Generate bilinear interpolation weights",
-    "              Generates bilinear interpolation weights for the first input field and writes the",
-    "              result to a file. The format of this file is NetCDF following the SCRIP convention.",
-    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
-    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
-    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
-    "",
-    "PARAMETER",
-    "    grid   STRING  Target grid description file or name",
-    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
-    "",
-    "ENVIRONMENT",
-    "    REMAP_EXTRAPOLATE",
-    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
-    "        By default the extrapolation is enabled for circular grids.",
-    nullptr
-};
-
-static const char *RemapbicHelp[] = {
-    "NAME",
-    "    remapbic, genbic - Bicubic interpolation",
-    "",
-    "SYNOPSIS",
-    "    remapbic,grid  infile outfile",
-    "    genbic,grid[,map3d]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators for a bicubic remapping of fields between grids in spherical coordinates.",
-    "    The interpolation is based on an adapted SCRIP library version. ",
-    "    For a detailed description of the interpolation method see SCRIP.",
-    "    This interpolation method only works on quadrilateral curvilinear source grids.",
-    "",
-    "OPERATORS",
-    "    remapbic  Bicubic interpolation",
-    "              Performs a bicubic interpolation on all input fields.",
-    "    genbic    Generate bicubic interpolation weights",
-    "              Generates bicubic interpolation weights for the first input field and writes the",
-    "              result to a file. The format of this file is NetCDF following the SCRIP convention.",
-    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
-    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
-    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
-    "",
-    "PARAMETER",
-    "    grid   STRING  Target grid description file or name",
-    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
-    "",
-    "ENVIRONMENT",
-    "    REMAP_EXTRAPOLATE",
-    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
-    "        By default the extrapolation is enabled for circular grids.",
-    nullptr
-};
-
-static const char *RemapnnHelp[] = {
-    "NAME",
-    "    remapnn, gennn - Nearest neighbor remapping",
-    "",
-    "SYNOPSIS",
-    "    remapnn,grid  infile outfile",
-    "    gennn,grid[,map3d]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators for a nearest neighbor remapping of fields between grids",
-    "    in spherical coordinates.",
-    "",
-    "OPERATORS",
-    "    remapnn  Nearest neighbor remapping",
-    "             Performs a nearest neighbor remapping on all input fields.",
-    "    gennn    Generate nearest neighbor remap weights",
-    "             Generates nearest neighbor remapping weights for the first input field and writes the result to a file.",
-    "             The format of this file is NetCDF following the SCRIP convention.",
-    "             Use the operator remap to apply this remapping weights to a data file with the same source grid.",
-    "             Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
-    "             In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
-    "",
-    "PARAMETER",
-    "    grid   STRING  Target grid description file or name",
-    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
-    "",
-    "ENVIRONMENT",
-    "    REMAP_EXTRAPOLATE    ",
-    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
-    "        By default the extrapolation is enabled for this remapping method.",
-    "    CDO_GRIDSEARCH_RADIUS",
-    "        Grid search radius in degree, default 180 degree.",
-    nullptr
-};
-
-static const char *RemapdisHelp[] = {
-    "NAME",
-    "    remapdis, gendis - Distance weighted average remapping",
-    "",
-    "SYNOPSIS",
-    "    remapdis,grid[,neighbors]  infile outfile",
-    "    gendis,grid[,neighbors[,map3d]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators for an inverse distance weighted average remapping of the four",
-    "    nearest neighbor values of fields between grids in spherical coordinates.",
-    "    The default number of 4 neighbors can be changed with the neighbors parameter.",
-    "",
-    "OPERATORS",
-    "    remapdis  Distance weighted average remapping",
-    "              Performs an inverse distance weighted averaged remapping of the nearest neighbor values on all input fields.",
-    "    gendis    Generate distance weighted average remap weights",
-    "              Generates distance weighted averaged remapping weights of the nearest neighbor values for the first input",
-    "              field and writes the result to a file. The format of this file is NetCDF following the SCRIP convention.",
-    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
-    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
-    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
-    "",
-    "PARAMETER",
-    "    grid       STRING   Target grid description file or name",
-    "    neighbors  INTEGER  Number of nearest neighbors [default: 4]",
-    "    map3d      BOOL    Generate all mapfiles of the first 3D field",
-    "",
-    "ENVIRONMENT",
-    "    REMAP_EXTRAPOLATE    ",
-    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
-    "        By default the extrapolation is enabled for this remapping method.",
-    "    CDO_GRIDSEARCH_RADIUS",
-    "        Grid search radius in degree, default 180 degree.",
-    nullptr
-};
-
-static const char *RemapconHelp[] = {
-    "NAME",
-    "    remapcon, gencon - First order conservative remapping",
-    "",
-    "SYNOPSIS",
-    "    remapcon,grid  infile outfile",
-    "    gencon,grid[,map3d]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators for a first order conservative remapping of fields between grids in spherical coordinates.",
-    "    The operators in this module uses code from the YAC software package to compute the conservative remapping weights.",
-    "    For a detailed description of the interpolation method see YAC.",
-    "    The interpolation method is completely general and can be used for any grid on a sphere.",
-    "    The search algorithm for the conservative remapping requires that no grid cell occurs more than once. ",
-    "",
-    "OPERATORS",
-    "    remapcon  First order conservative remapping",
-    "              Performs a first order conservative remapping on all input fields.",
-    "    gencon    Generate 1st order conservative remap weights",
-    "              Generates first order conservative remapping weights for the first input field and",
-    "              writes the result to a file. The format of this file is NetCDF following the SCRIP convention.",
-    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
-    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with variing masks.",
-    "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
-    "",
-    "PARAMETER",
-    "    grid   STRING  Target grid description file or name",
-    "    map3d  BOOL    Generate all mapfiles of the first 3D field",
-    "",
-    "ENVIRONMENT",
-    "    CDO_REMAP_NORM",
-    "        This variable is used to choose the normalization of the conservative interpolation. ",
-    "        By default CDO_REMAP_NORM is set to 'fracarea'. 'fracarea' uses the sum of the",
-    "        non-masked source cell intersected areas to normalize each target cell field value.",
-    "        This results in a reasonable flux value but the flux is not locally conserved.",
-    "        The option 'destarea' uses the total target cell area to normalize each target cell",
-    "        field value. Local flux conservation is ensured, but unreasonable flux values may result.",
-    "    REMAP_AREA_MIN",
-    "        This variable is used to set the minimum destination area fraction. The default",
-    "        of this variable is 0.0.",
-    nullptr
-};
-
-static const char *RemaplafHelp[] = {
-    "NAME",
-    "    remaplaf, genlaf - Largest area fraction remapping",
-    "",
-    "SYNOPSIS",
-    "    <operator>,grid  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators for a largest area fraction remapping of fields between grids in spherical coordinates.",
-    "    The operators in this module uses code from the YAC software package to compute the largest area fraction.",
-    "    For a detailed description of the interpolation method see YAC.",
-    "    The interpolation method is completely general and can be used for any grid on a sphere.",
-    "    The search algorithm for this remapping method requires that no grid cell occurs more than once. ",
-    "",
-    "OPERATORS",
-    "    remaplaf  Largest area fraction remapping",
-    "              Performs a largest area fraction remapping on all input fields.",
-    "    genlaf    Generate largest area fraction remap weights",
-    "              Generates largest area fraction remapping weights for the first input field and",
-    "              writes the result to a file. The format of this file is NetCDF following the SCRIP convention.",
-    "              Use the operator remap to apply this remapping weights to a data file with the same source grid.",
-    "",
-    "PARAMETER",
-    "    grid  STRING  Target grid description file or name",
-    "",
-    "ENVIRONMENT",
-    "    REMAP_AREA_MIN",
-    "        This variable is used to set the minimum destination area fraction. The default",
-    "        of this variable is 0.0.",
-    nullptr
-};
-
-static const char *RemapHelp[] = {
-    "NAME",
-    "    remap - Grid remapping",
-    "",
-    "SYNOPSIS",
-    "    remap,grid,weights  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Interpolation between different horizontal grids can be a very time-consuming ",
-    "    process. Especially if the data are on an unstructured and/or a large grid. ",
-    "    In this case the interpolation process can be split into two parts.",
-    "    Firstly the generation of the interpolation weights, which is the most time-consuming part.",
-    "    These interpolation weights can be reused for every remapping process with the operator remap.",
-    "    This operator remaps all input fields to a new horizontal grid. The remap type and ",
-    "    the interpolation weights of one input grid are read from a NetCDF file. More weights ",
-    "    are computed if the input fields are on different grids. The NetCDF file with the ",
-    "    weights should follow the SCRIP convention. Normally these weights come from a previous",
-    "    call to one of the genXXX operators (e.g. genbil) or were created by the original SCRIP package.",
-    "",
-    "PARAMETER",
-    "    grid     STRING  Target grid description file or name",
-    "    weights  STRING  Interpolation weights (SCRIP NetCDF file)",
-    "",
-    "ENVIRONMENT",
-    "    CDO_REMAP_NORM       ",
-    "        This variable is used to choose the normalization of the conservative interpolation. ",
-    "        By default CDO_REMAP_NORM is set to 'fracarea'. 'fracarea' uses the sum of the",
-    "        non-masked source cell intersected areas to normalize each target cell field value.",
-    "        This results in a reasonable flux value but the flux is not locally conserved.",
-    "        The option 'destarea' uses the total target cell area to normalize each target cell",
-    "        field value. Local flux conservation is ensured, but unreasonable flux values may result.",
-    "    REMAP_EXTRAPOLATE    ",
-    "        This variable is used to switch the extrapolation feature 'on' or 'off'.",
-    "        By default the extrapolation is enabled for remapdis, remapnn and for circular grids.",
-    "    REMAP_AREA_MIN       ",
-    "        This variable is used to set the minimum destination area fraction. The default",
-    "        of this variable is 0.0.",
-    "    CDO_GRIDSEARCH_RADIUS",
-    "        Grid search radius in degree, default 180 degree.",
-    nullptr
-};
-
-static const char *RemapetaHelp[] = {
-    "NAME",
-    "    remapeta - Remap vertical hybrid level",
-    "",
-    "SYNOPSIS",
-    "    remapeta,vct[,oro]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator interpolates between different vertical hybrid levels. This include the preparation ",
-    "    of consistent data for the free atmosphere. The procedure for the vertical interpolation is based ",
-    "    on the HIRLAM scheme and was adapted from INTERA.",
-    "    The vertical interpolation is based on the vertical integration of the hydrostatic equation with ",
-    "    few adjustments. The basic tasks are the following one:",
-    "    - at first integration of hydrostatic equation",
-    "    - extrapolation of surface pressure",
-    "    - Planetary Boundary-Layer (PBL) proutfile interpolation",
-    "    - interpolation in free atmosphere",
-    "    - merging of both proutfiles",
-    "    - final surface pressure correction",
-    "    ",
-    "    The vertical interpolation corrects the surface pressure. This is simply a cut-off or an addition ",
-    "    of air mass. This mass correction should not influence the geostrophic velocity field in the middle ",
-    "    troposhere. Therefore the total mass above a given reference level is conserved. As reference level",
-    "    the geopotential height of the 400 hPa level is used. Near the surface the correction can affect ",
-    "    the vertical structure of the PBL. Therefore the interpolation is done using the potential temperature. ",
-    "    But in the free atmosphere above a certain n (n=0.8 defining the top of the PBL) the interpolation ",
-    "    is done linearly. After the interpolation both proutfiles are merged. With the resulting ",
-    "    temperature/pressure correction the hydrostatic equation is integrated again and adjusted to the ",
-    "    reference level finding the final surface pressure correction. A more detailed description of",
-    "    the interpolation can be found in INTERA. This operator requires all variables on the same horizontal grid.",
-    "",
-    "PARAMETER",
-    "    vct  STRING  File name of an ASCII dataset with the vertical coordinate table",
-    "    oro  STRING  File name with the orography (surf. geopotential) of the target dataset (optional)",
-    "",
-    "ENVIRONMENT",
-    "    REMAPETA_PTOP",
-    "        Sets the minimum pressure level for condensation.",
-    "        Above this level the humidity is set to the constant 1.E-6.",
-    "        The default value is 0 Pa.",
-    "",
-    "NOTE",
-    "    The code numbers or the variable names of the required parameter have to follow the ECHAM convention.",
-    "    ",
-    "    Use the sinfo command to test if your vertical coordinate system is recognized as hybrid system.",
-    "    ",
-    "    In case remapeta complains about not finding any data on hybrid model levels you may wish",
-    "    to use the setzaxis command to generate a zaxis description which conforms to the ECHAM convention.",
-    "    See section \"1.4 Z-axis description\" for an example how to define a hybrid Z-axis.",
-    nullptr
-};
-
-static const char *VertintmlHelp[] = {
-    "NAME",
-    "    ml2pl, ml2hl - Vertical interpolation",
-    "",
-    "SYNOPSIS",
-    "    ml2pl,plevels  infile outfile",
-    "    ml2hl,hlevels  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Interpolates 3D variables on hybrid sigma pressure level to pressure or height levels.",
-    "    The input file should contain the log. surface pressure or the surface pressure.",
-    "    To extrapolate the temperature, the surface geopotential is also needed.",
-    "    It is assumed that the geopotential heights are located at the hybrid layer interfaces.",
-    "    For the lowest layer of geopotential heights the surface geopotential is required.",
-    "    The pressure, temperature, geopotential height, and surface geopotential are identified by",
-    "    their GRIB1 code number or NetCDF CF standard name.",
-    "    Supported parameter tables are: WMO standard table number 2 and ECMWF local table number 128.",
-    "    ",
-    "     CF standard name            & Units      & GRIB 1 code      ",
-    "       surface_air_pressure      &  Pa        &  134",
-    "       air_temperature           &  K         &  130",
-    "       surface_geopotential      &  m2 s-2    &  129",
-    "       geopotential_height       &  m         &  156",
-    "    ",
-    "    Use the alias  ml2plx/ml2hlx or the environment variable EXTRAPOLATE to extrapolate",
-    "    missing values. This operator requires all variables on the same horizontal grid.",
-    "    Missing values in the input data are not supported.",
-    "    ",
-    "",
-    "OPERATORS",
-    "    ml2pl  Model to pressure level interpolation",
-    "           Interpolates 3D variables on hybrid sigma pressure level to pressure level.",
-    "    ml2hl  Model to height level interpolation",
-    "           Interpolates 3D variables on hybrid sigma pressure level to height level.",
-    "           The procedure is the same as for the operator ml2pl except for",
-    "           the pressure levels being calculated from the heights by:",
-    "           plevel = 101325*exp(hlevel/-7000)",
-    "",
-    "PARAMETER",
-    "    plevels  FLOAT  Pressure levels in pascal",
-    "    hlevels  FLOAT  Height levels in meter",
-    "",
-    "ENVIRONMENT",
-    "    EXTRAPOLATE",
-    "        If set to 1 extrapolate missing values.",
-    "",
-    "NOTE",
-    "    The components of the hybrid coordinate must always be avaiable at the hybrid layer interfaces even if the data is defined at the hybrid layer midpoints.",
-    nullptr
-};
-
-static const char *VertintapHelp[] = {
-    "NAME",
-    "    ap2pl - Vertical pressure interpolation",
-    "",
-    "SYNOPSIS",
-    "    ap2pl,plevels  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Interpolate 3D variables on hybrid sigma height coordinates to pressure levels.",
-    "    The input file must contain the 3D air pressure in pascal. The air pressure is",
-    "    identified by the NetCDF CF standard name air_pressure.",
-    "    Use the alias  ap2plx or the environment variable EXTRAPOLATE to extrapolate",
-    "    missing values. This operator requires all variables on the same horizontal grid.",
-    "",
-    "PARAMETER",
-    "    plevels  FLOAT  Comma-separated list of pressure levels in pascal",
-    "",
-    "ENVIRONMENT",
-    "    EXTRAPOLATE",
-    "        If set to 1 extrapolate missing values.",
-    "",
-    "NOTE",
-    "    This is a specific implementation for NetCDF files from the ICON model, it may not work with data from other sources.",
-    nullptr
-};
-
-static const char *VertintghHelp[] = {
-    "NAME",
-    "    gh2hl - Vertical height interpolation",
-    "",
-    "SYNOPSIS",
-    "    gh2hl,hlevels  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Interpolate 3D variables on hybrid sigma height coordinates to height levels.",
-    "    The input file must contain the 3D geometric height in meter. The geometric height is",
-    "    identified by the NetCDF CF standard name geometric_height_at_full_level_center.",
-    "    Use the alias  gh2hlx or the environment variable EXTRAPOLATE to extrapolate",
-    "    missing values. This operator requires all variables on the same horizontal grid.",
-    "",
-    "PARAMETER",
-    "    hlevels  FLOAT  Comma-separated list of height levels in meter",
-    "",
-    "ENVIRONMENT",
-    "    EXTRAPOLATE",
-    "        If set to 1 extrapolate missing values.",
-    "",
-    "NOTE",
-    "    This is a specific implementation for NetCDF files from the ICON model, it may not work with data from other sources.",
-    nullptr
-};
-
-static const char *IntlevelHelp[] = {
-    "NAME",
-    "    intlevel - Linear level interpolation",
-    "",
-    "SYNOPSIS",
-    "    intlevel,parameter  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator performs a linear vertical interpolation of 3D variables. The target levels can be",
-    "    specified with the level parameter or read in via a Z-axis description file.",
-    "",
-    "PARAMETER",
-    "    level  FLOAT   Comma-separated list of target levels",
-    "    file   STRING  Path to a file containing a description of the Z-axis",
-    nullptr
-};
-
-static const char *Intlevel3dHelp[] = {
-    "NAME",
-    "    intlevel3d, intlevelx3d - ",
-    "    Linear level interpolation from/to 3D vertical coordinates",
-    "",
-    "SYNOPSIS",
-    "    <operator>,tgtcoordinate  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator performs a linear vertical interpolation of 3D variables fields with given 3D vertical coordinates.",
-    "    infile1 contains the 3D data variables and infile2 the 3D vertical source coordinate. The parameter tgtcoordinate",
-    "    is a datafile with the 3D vertical target coordinate.",
-    "",
-    "OPERATORS",
-    "    intlevel3d   Linear level interpolation onto a 3D vertical coordinate",
-    "    intlevelx3d  like intlevel3d but with extrapolation",
-    "",
-    "PARAMETER",
-    "    tgtcoordinate  STRING  filename for 3D vertical target coordinates",
-    nullptr
-};
-
-static const char *InttimeHelp[] = {
-    "NAME",
-    "    inttime, intntime - Time interpolation",
-    "",
-    "SYNOPSIS",
-    "    inttime,date,time[,inc]  infile outfile",
-    "    intntime,n  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module performs linear interpolation between timesteps.",
-    "    Interpolation is only performed if both values exist.",
-    "    If both values are missing values, the result is also a missing value.",
-    "    If only one value exists, it is taken if the time weighting is greater than or equal to 0.5.",
-    "    So no new value will be created at existing time steps, if the value is missing there.",
-    "",
-    "OPERATORS",
-    "    inttime   Interpolation between timesteps",
-    "              This operator creates a new dataset by linear interpolation between timesteps.",
-    "              The user has to define the start date/time with an optional increment.",
-    "    intntime  Interpolation between timesteps",
-    "              This operator performs linear interpolation between timesteps.",
-    "              The user has to define the number of timesteps from one timestep to the next.",
-    "",
-    "PARAMETER",
-    "    date  STRING  Start date (format YYYY-MM-DD)",
-    "    time  STRING  Start time (format hh:mm:ss)",
-    "    inc   STRING  Optional increment (seconds, minutes, hours, days, months, years) [default: 0hour]",
-    "    n     INTEGER Number of timesteps from one timestep to the next",
-    nullptr
-};
-
-static const char *IntyearHelp[] = {
-    "NAME",
-    "    intyear - Year interpolation",
-    "",
-    "SYNOPSIS",
-    "    intyear,years  infile1 infile2 obase",
-    "",
-    "DESCRIPTION",
-    "    This operator performs linear interpolation between two years, timestep by timestep.",
-    "    The input files need to have the same structure with the same variables.",
-    "    The output files will be named <obase><yyyy><suffix> where yyyy will be the year and ",
-    "    suffix is the filename extension derived from the file format.",
-    "",
-    "PARAMETER",
-    "    years  INTEGER  Comma-separated list or first/last[/inc] range of years",
-    "",
-    "ENVIRONMENT",
-    "    CDO_FILE_SUFFIX",
-    "        Set the default file suffix. This suffix will be added to the output file ",
-    "        names instead of the filename extension derived from the file format. ",
-    "        Set this variable to NULL to disable the adding of a file suffix.",
-    "",
-    "NOTE",
-    "    This operator needs to open all output files simultaneously.",
-    "    The maximum number of open files depends on the operating system!",
-    nullptr
-};
-
-static const char *SpectralHelp[] = {
-    "NAME",
-    "    sp2gp, gp2sp - Spectral transformation",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,type|trunc]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module transforms fields on a global regular Gaussian grid to spectral coefficients and vice versa.",
-    "    The transformation is achieved by applying Fast Fourier Transformation (FFT) first and direct Legendre",
-    "    Transformation afterwards in gp2sp. In sp2gp the inverse Legendre Transformation and inverse FFT are used.",
-    "    Missing values are not supported.",
-    "    ",
-    "    The relationship between the spectral resolution, governed by the truncation number T, and the grid",
-    "    resolution depends on the number of grid points at which the shortest wavelength field is represented.",
-    "    For a grid with 2N points between the poles (so 4N grid points in total around the globe) the relationship is:",
-    "    ",
-    "             linear grid: the shortest wavelength is represented by 2 grid points → 4N ≃ 2(TL + 1)",
-    "    ",
-    "          quadratic grid: the shortest wavelength is represented by 3 grid points → 4N ≃ 3(TQ + 1)",
-    "    ",
-    "              cubic grid: the shortest wavelength is represented by 4 grid points → 4N ≃ 4(TC + 1)",
-    "    ",
-    "    The quadratic grid is used by ECHAM and ERA15. ERA40 is using a linear Gaussian grid reflected by the TL notation.",
-    "    ",
-    "    The following table shows the calculation of the number of latitudes and the triangular truncation for the different grid types:",
-    "    ",
-    "         Gridtype           & Number of latitudes: nlat   & Triangular truncation: ntr  ",
-    "           linear           &     NINT((ntr*2 + 1)/2)     &     (nlat*2 - 1) / 2",
-    "           quadratic        &     NINT((ntr*3 + 1)/2)     &     (nlat*2 - 1) / 3",
-    "           cubic            &     NINT((ntr*4 + 1)/2)     &     (nlat*2 - 1) / 4",
-    "",
-    "OPERATORS",
-    "    sp2gp  Spectral to gridpoint",
-    "           Convert all spectral fields to a global regular Gaussian grid.",
-    "           The optional parameter trunc must be greater than the input truncation.",
-    "    gp2sp  Gridpoint to spectral",
-    "           Convert all Gaussian gridpoint fields to spectral fields.",
-    "           The optional parameter trunc must be lower than the input truncation.",
-    "",
-    "PARAMETER",
-    "    type   STRING  Type of the grid: quadratic, linear, cubic (default: type=quadratic)",
-    "    trunc  STRING  Triangular truncation",
-    "",
-    "NOTE",
-    "    To speed up the calculations, the Legendre polynoms are kept in memory. This requires a relatively large",
-    "    amount of memory. This is for example 12GB for T1279 data.",
-    nullptr
-};
-
-static const char *SpecconvHelp[] = {
-    "NAME",
-    "    sp2sp - Spectral conversion",
-    "",
-    "SYNOPSIS",
-    "    sp2sp,trunc  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Changed the triangular truncation of all spectral fields. This operator performs downward ",
-    "    conversion by cutting the resolution. Upward conversions are achieved by filling in zeros.",
-    "",
-    "PARAMETER",
-    "    trunc  INTEGER  New spectral resolution",
-    nullptr
-};
-
-static const char *Wind2Help[] = {
-    "NAME",
-    "    dv2ps - D and V to velocity potential and stream function",
-    "",
-    "SYNOPSIS",
-    "    dv2ps  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Calculate spherical harmonic coefficients of velocity potential and stream function from ",
-    "    spherical harmonic coefficients of relative divergence and vorticity. The divergence and ",
-    "    vorticity need to have the names sd and svo or code numbers 155 and 138.",
-    nullptr
-};
-
-static const char *WindHelp[] = {
-    "NAME",
-    "    dv2uv, uv2dv - Wind transformation",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,gridtype]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module converts relative divergence and vorticity to U and V wind and vice versa.",
-    "    Divergence and vorticity are spherical harmonic coefficients in spectral space and",
-    "    U and V are on a global regular Gaussian grid. The Gaussian latitudes need to be ordered from",
-    "    north to south. Missing values are not supported.",
-    "    ",
-    "    The relationship between the spectral resolution, governed by the truncation number T, and the grid",
-    "    resolution depends on the number of grid points at which the shortest wavelength field is represented.",
-    "    For a grid with 2N points between the poles (so 4N grid points in total around the globe) the relationship is:",
-    "    ",
-    "             linear grid: the shortest wavelength is represented by 2 grid points → 4N ≃ 2(TL + 1)",
-    "    ",
-    "          quadratic grid: the shortest wavelength is represented by 3 grid points → 4N ≃ 3(TQ + 1)",
-    "    ",
-    "              cubic grid: the shortest wavelength is represented by 4 grid points → 4N ≃ 4(TC + 1)",
-    "    ",
-    "    The quadratic grid is used by ECHAM and ERA15. ERA40 is using a linear Gaussian grid reflected by the TL notation.",
-    "    ",
-    "    The following table shows the calculation of the number of latitudes and the triangular truncation for the different grid types:",
-    "    ",
-    "         Gridtype           & Number of latitudes: nlat   & Triangular truncation: ntr  ",
-    "           linear           &     NINT((ntr*2 + 1)/2)     &     (nlat*2 - 1) / 2",
-    "           quadratic        &     NINT((ntr*3 + 1)/2)     &     (nlat*2 - 1) / 3",
-    "           cubic            &     NINT((ntr*4 + 1)/2)     &     (nlat*2 - 1) / 4",
-    "",
-    "OPERATORS",
-    "    dv2uv  Divergence and vorticity to U and V wind",
-    "           Calculate U and V wind on a Gaussian grid from spherical harmonic ",
-    "           coefficients of relative divergence and vorticity. The divergence and vorticity ",
-    "           need to have the names sd and svo or code numbers 155 and 138.",
-    "    uv2dv  U and V wind to divergence and vorticity",
-    "           Calculate spherical harmonic coefficients of relative divergence and vorticity",
-    "           from U and V wind. The U and V wind need to be on a Gaussian grid and need to have the ",
-    "           names u and v or the code numbers 131 and 132.",
-    "",
-    "PARAMETER",
-    "    gridtype  STRING  Type of the grid: quadratic, linear (default: quadratic)",
-    "",
-    "NOTE",
-    "    To speed up the calculations, the Legendre polynoms are kept in memory. This requires a relatively large",
-    "    amount of memory. This is for example 12GB for T1279 data.",
-    nullptr
-};
-
-static const char *FourierHelp[] = {
-    "NAME",
-    "    fourier - Fourier transformation",
-    "",
-    "SYNOPSIS",
-    "    fourier,epsilon  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    The fourier operator performs the fourier transformation or the inverse fourier transformation of all input fields.",
-    "    If the number of timesteps is a power of 2 then the algorithm of the Fast Fourier Transformation (FFT) is used.",
-    "    ",
-    "    ",
-    "    If the input stream infile consists only of complex fields, then the fields of outfile, computed by",
-    "       cdo -f ext fourier,1 -fourier,-1 infile outfile",
-    "    are the same than that of infile. For real input files see function retocomplex.",
-    "",
-    "PARAMETER",
-    "    epsilon  INTEGER  -1: forward transformation;  1: backward transformation",
-    "",
-    "NOTE",
-    "    Complex numbers can only be stored in NetCDF4 and EXTRA format.",
-    nullptr
-};
-
-static const char *ImportbinaryHelp[] = {
-    "NAME",
-    "    import_binary - Import binary data sets",
-    "",
-    "SYNOPSIS",
-    "    import_binary  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator imports gridded binary data sets via a GrADS data descriptor file.",
-    "    The GrADS data descriptor file contains a complete description of the binary data as well ",
-    "    as instructions on where to find the data and how to read it. The descriptor file is an ASCII ",
-    "    file that can be created easily with a text editor. The general contents of a gridded data ",
-    "    descriptor file are as follows:",
-    "    - Filename for the binary data",
-    "    - Missing or undefined data value",
-    "    - Mapping between grid coordinates and world coordinates",
-    "    - Description of variables in the binary data set ",
-    "    ",
-    "    A detailed description of the components of a GrADS data descriptor file can be found in GrADS.",
-    "    Here is a list of the supported components:",
-    "    BYTESWAPPED, CHSUB, DSET, ENDVARS, FILEHEADER, HEADERBYTES, OPTIONS, TDEF, TITLE, ",
-    "    TRAILERBYTES, UNDEF, VARS, XDEF, XYHEADER, YDEF, ZDEF",
-    "",
-    "NOTE",
-    "    Only 32-bit IEEE floats are supported for standard binary files!",
-    nullptr
-};
-
-static const char *ImportcmsafHelp[] = {
-    "NAME",
-    "    import_cmsaf - Import CM-SAF HDF5 files",
-    "",
-    "SYNOPSIS",
-    "    import_cmsaf  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator imports gridded CM-SAF (Satellite Application Facility on Climate Monitoring)",
-    "    HDF5 files. CM-SAF exploits data from polar-orbiting and geostationary satellites in order ",
-    "    to provide climate monitoring products of the following parameters: ",
-    "    ",
-    "    Cloud parameters: cloud fraction (CFC), cloud type (CTY), cloud phase (CPH), ",
-    "                      cloud top height, pressure and temperature (CTH,CTP,CTT), ",
-    "                      cloud optical thickness (COT), cloud water path (CWP).",
-    "    ",
-    "    Surface radiation components: Surface albedo (SAL); surface incoming (SIS) ",
-    "                      and net (SNS) shortwave radiation; surface downward (SDL) ",
-    "                      and outgoing (SOL) longwave radiation, surface net longwave ",
-    "                      radiation (SNL) and surface radiation budget (SRB).",
-    "    ",
-    "    Top-of-atmosphere radiation components: Incoming (TIS) and reflected (TRS) ",
-    "                      solar radiative flux at top-of-atmosphere. Emitted thermal ",
-    "                      radiative flux at top-of-atmosphere (TET).",
-    "    ",
-    "    Water vapour:     Vertically integrated water vapour (HTW), layered vertically ",
-    "                      integrated water vapour and layer mean temperature and relative ",
-    "                      humidity for 5 layers (HLW), temperature and mixing ratio at ",
-    "                      6 pressure levels. ",
-    "    ",
-    "    Daily and monthly mean products can be ordered via the CM-SAF web page (www.cmsaf.eu). ",
-    "    Products with higher spatial and temporal resolution, i.e. instantaneous swath-based products,",
-    "    are available on request (contact.cmsaf@dwd.de). All products are distributed free-of-charge.",
-    "    More information on the data is available on the CM-SAF homepage (www.cmsaf.eu).",
-    "    ",
-    "    Daily and monthly mean products are provided in equal-area projections. CDO reads the ",
-    "    projection parameters from the metadata in the HDF5-headers in order to allow spatial ",
-    "    operations like remapping. For spatial operations with instantaneous products on original ",
-    "    satellite projection, additional files with arrays of latitudes and longitudes are needed.",
-    "    These can be obtained from CM-SAF together with the data.",
-    "    ",
-    "",
-    "NOTE",
-    "    To use this operator, it is necessary to build CDO with HDF5 support (version 1.6 or higher).",
-    "    The PROJ library (version 5.0 or higher) is needed for full support of the remapping",
-    "    functionality. ",
-    nullptr
-};
-
-static const char *ImportamsrHelp[] = {
-    "NAME",
-    "    import_amsr - Import AMSR binary files",
-    "",
-    "SYNOPSIS",
-    "    import_amsr  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator imports gridded binary AMSR (Advanced Microwave Scanning Radiometer) data.",
-    "    The binary data files are available from the AMSR ftp site (ftp://ftp.ssmi.com/amsre).",
-    "    Each file consists of twelve (daily) or five (averaged) 0.25 x 0.25 degree ",
-    "    grid (1440,720) byte maps. For daily files, six daytime maps in the following",
-    "    order, Time (UTC), Sea Surface Temperature (SST), 10 meter Surface Wind Speed (WSPD),",
-    "    Atmospheric Water Vapor (VAPOR), Cloud Liquid Water (CLOUD), and Rain Rate (RAIN), ",
-    "    are followed by six nighttime maps in the same order. Time-Averaged files contain ",
-    "    just the geophysical layers in the same order [SST, WSPD, VAPOR, CLOUD, RAIN].",
-    "    More information to the data is available on the AMSR homepage http://www.remss.com/amsr.",
-    nullptr
-};
-
-static const char *InputHelp[] = {
-    "NAME",
-    "    input, inputsrv, inputext - Formatted input",
-    "",
-    "SYNOPSIS",
-    "    input,grid[,zaxis]  outfile",
-    "    inputsrv  outfile",
-    "    inputext  outfile",
-    "",
-    "DESCRIPTION",
-    "    This module reads time series of one 2D variable from standard input.",
-    "    All input fields need to have the same horizontal grid. The format of the ",
-    "    input depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    input     ASCII input",
-    "              Reads fields with ASCII numbers from standard input and stores them",
-    "              in outfile. The numbers read are exactly that ones which are written ",
-    "              out by the output operator.",
-    "    inputsrv  SERVICE ASCII input",
-    "              Reads fields with ASCII numbers from standard input and stores them ",
-    "              in outfile. Each field should have a header of 8 integers (SERVICE likely).",
-    "              The numbers that are read are exactly that ones which are written out by ",
-    "              the outputsrv operator.",
-    "    inputext  EXTRA ASCII input",
-    "              Read fields with ASCII numbers from standard input and stores them ",
-    "              in outfile. Each field should have header of 4 integers (EXTRA likely).",
-    "              The numbers read are exactly that ones which are written out by ",
-    "              the outputext operator.",
-    "",
-    "PARAMETER",
-    "    grid   STRING  Grid description file or name",
-    "    zaxis  STRING  Z-axis description file",
-    nullptr
-};
-
-static const char *OutputHelp[] = {
-    "NAME",
-    "    output, outputf, outputint, outputsrv, outputext - Formatted output",
-    "",
-    "SYNOPSIS",
-    "    output  infiles",
-    "    outputf,format[,nelem]  infiles",
-    "    outputint  infiles",
-    "    outputsrv  infiles",
-    "    outputext  infiles",
-    "",
-    "DESCRIPTION",
-    "    This module prints all values of all input datasets to standard output.",
-    "    All input fields need to have the same horizontal grid. All input files ",
-    "    need to have the same structure with the same variables.",
-    "    The format of the output depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    output     ASCII output",
-    "               Prints all values to standard output.",
-    "               Each row has 6 elements with the C-style format \"%13.6g\".",
-    "    outputf    Formatted output",
-    "               Prints all values to standard output.",
-    "               The format and number of elements for each row have to be specified by the parameters",
-    "               format and nelem. The default for nelem is 1.",
-    "    outputint  Integer output",
-    "               Prints all values rounded to the nearest integer to standard output.",
-    "    outputsrv  SERVICE ASCII output",
-    "               Prints all values to standard output.",
-    "               Each field with a header of 8 integers (SERVICE likely).",
-    "    outputext  EXTRA ASCII output",
-    "               Prints all values to standard output.",
-    "               Each field with a header of 4 integers (EXTRA likely).",
-    "",
-    "PARAMETER",
-    "    format  STRING  C-style format for one element (e.g. %13.6g)",
-    "    nelem   INTEGER Number of elements for each row (default: nelem = 1)",
-    nullptr
-};
-
-static const char *OutputtabHelp[] = {
-    "NAME",
-    "    outputtab - Table output",
-    "",
-    "SYNOPSIS",
-    "    outputtab,parameter  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator prints a table of all input datasets to standard output.",
-    "    infiles is an arbitrary number of input files. All input files need to have ",
-    "    the same structure with the same variables on different timesteps.",
-    "    All input fields need to have the same horizontal grid.",
-    "    ",
-    "    The contents of the table depends on the chosen parameters. The format of each table parameter is keyname[:len].",
-    "    len is the optional length of a table entry. The number of significant digits of floating point parameters",
-    "    can be set with the CDO option --precision, the default is 7.",
-    "    Here is a list of all valid keynames:",
-    "    ",
-    "     Keyname    & Type    & Description      ",
-    "     value      & FLOAT   & Value of the variable [len:8]",
-    "     name       & STRING  & Name of the variable [len:8]",
-    "     param      & STRING  & Parameter ID (GRIB1: code[.tabnum]; GRIB2: num[.cat[.dis]]) [len:11]",
-    "     code       & INTEGER & Code number [len:4]",
-    "     x          & FLOAT   & X coordinate of the original grid [len:6]",
-    "     y          & FLOAT   & Y coordinate of the original grid [len:6]",
-    "     lon        & FLOAT   & Longitude coordinate in degrees [len:6]",
-    "     lat        & FLOAT   & Latitude coordinate in degrees [len:6]",
-    "     lev        & FLOAT   & Vertical level [len:6]",
-    "     xind       & INTEGER & Grid x index [len:4]",
-    "     yind       & INTEGER & Grid y index [len:4]",
-    "     timestep   & INTEGER & Timestep number [len:6]",
-    "     date       & STRING  & Date (format YYYY-MM-DD) [len:10]",
-    "     time       & STRING  & Time (format hh:mm:ss) [len:8]",
-    "     year       & INTEGER & Year [len:5]",
-    "     month      & INTEGER & Month [len:2]",
-    "     day        & INTEGER & Day [len:2]",
-    "     nohead     & INTEGER & Disable output of header line",
-    "",
-    "PARAMETER",
-    "    parameter  STRING   Comma-separated list of keynames, one for each column of the table",
-    nullptr
-};
-
-static const char *OutputgmtHelp[] = {
-    "NAME",
-    "    gmtxyz, gmtcells - GMT output",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile",
-    "",
-    "DESCRIPTION",
-    "    This module prints the first field of the input dataset to standard output.",
-    "    The output can be used to generate 2D Lon/Lat plots with GMT.",
-    "    The format of the output depends on the chosen operator.",
-    "",
-    "OPERATORS",
-    "    gmtxyz    GMT xyz format",
-    "              The operator exports the first field to the GMT xyz ASCII format.",
-    "              The output can be used to create contour plots with the GMT module pscontour.",
-    "    gmtcells  GMT multiple segment format",
-    "              The operator exports the first field to the GMT multiple segment ASCII format.",
-    "              The output can be used to create shaded gridfill plots with the GMT module psxy.",
-    nullptr
-};
-
-static const char *GradsdesHelp[] = {
-    "NAME",
-    "    gradsdes - GrADS data descriptor file",
-    "",
-    "SYNOPSIS",
-    "    gradsdes[,mapversion]  infile",
-    "",
-    "DESCRIPTION",
-    "    Creates a GrADS data descriptor file. Supported file formats are GRIB1, NetCDF, SERVICE, ",
-    "    EXTRA and IEG. For GRIB1 files the GrADS map file is also generated. For SERVICE and EXTRA",
-    "    files the grid have to be specified with the CDO option '-g <grid>'. This module takes infile",
-    "    in order to create filenames for the descriptor (infile.ctl) and the map (infile.gmp) file.",
-    "",
-    "PARAMETER",
-    "    mapversion  INTEGER  Format version of the GrADS map file for GRIB1 datasets. Use 1 for a machine",
-    "                specific version 1 GrADS map file, 2 for a machine independent version 2 GrADS map file",
-    "                and 4 to support GRIB files >2GB. ",
-    "                A version 2 map file can be used only with GrADS version 1.8 or newer.",
-    "                A version 4 map file can be used only with GrADS version 2.0 or newer.",
-    "                The default is 4 for files >2GB, otherwise 2.",
-    nullptr
-};
-
-static const char *AfterburnerHelp[] = {
-    "NAME",
-    "    after - ECHAM standard post processor",
-    "",
-    "SYNOPSIS",
-    "    after[,vct]  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    The \"afterburner\" is the standard post processor for ECHAM GRIB and NetCDF data which provides the following operations:",
-    "    ",
-    "    - Extract specified variables and levels",
-    "    - Compute derived variables",
-    "    - Transform spectral data to Gaussian grid representation",
-    "    - Vertical interpolation to pressure levels",
-    "    - Compute temporal means",
-    "    ",
-    "    This operator reads selection parameters as namelist from stdin.",
-    "    Use the UNIX redirection \"<namelistfile\" to read the namelist from file.",
-    "    ",
-    "    The input files can't be combined with other CDO operators because of an optimized reader for this operator.",
-    "",
-    "NAMELIST",
-    "    Namelist parameter and there defaults:",
-    "      TYPE=0, CODE=-1, LEVEL=-1, INTERVAL=0, MEAN=0, EXTRAPOLATE=1",
-    "    ",
-    "    TYPE controls the transformation and vertical interpolation. Transforming spectral data to Gaussian grid",
-    "    representation and vertical interpolation to pressure levels are performed in a chain of steps.",
-    "    The TYPE parameter may be used to stop the chain at a certain step. Valid values are:",
-    "    ",
-    "      TYPE  =  0 : Hybrid   level spectral coefficients",
-    "      TYPE  = 10 : Hybrid   level fourier  coefficients",
-    "      TYPE  = 11 : Hybrid   level zonal mean sections",
-    "      TYPE  = 20 : Hybrid   level gauss grids",
-    "      TYPE  = 30 : Pressure level gauss grids",
-    "      TYPE  = 40 : Pressure level fourier  coefficients",
-    "      TYPE  = 41 : Pressure level zonal mean sections",
-    "      TYPE  = 50 : Pressure level spectral coefficients",
-    "      TYPE  = 60 : Pressure level fourier  coefficients",
-    "      TYPE  = 61 : Pressure level zonal mean sections",
-    "      TYPE  = 70 : Pressure level gauss grids",
-    "    ",
-    "    Vorticity, divergence, streamfunction and velocity potential need special treatment in the vertical transformation.",
-    "    They are not available as types 30, 40 and 41. If you select one of these combinations, type is automatically",
-    "    switched to the equivalent types 70, 60 and 61. The type of all other variables will be switched too, because ",
-    "    the type is a global parameter.",
-    "    ",
-    "    CODE selects the variables by the ECHAM GRIB1 code number (1-255). The default value -1 processes all detected codes.",
-    "    Derived variables computed by the afterburner:",
-    "    ",
-    "    Code  & Name      & Longname                       & Units & Level       & Needed Codes",
-    "     34   & low_cld   & low cloud                      &       & single      & 223 on modellevel  ",
-    "     35   & mid_cld   & mid cloud                      &       & single      & 223 on modellevel  ",
-    "     36   & hih_cld   & high cloud                     &       & single      & 223 on modellevel  ",
-    "     131  & u         & u-velocity                     & m/s   & atm (ml+pl) & 138, 155           ",
-    "     132  & v         & v-velocity                     & m/s   & atm (ml+pl) & 138, 155           ",
-    "     135  & omega     & vertical velocity              & Pa/s  & atm (ml+pl) & 138, 152, 155      ",
-    "     148  & stream    & streamfunction                 & m^2/s & atm (ml+pl) & 131, 132           ",
-    "     149  & velopot   & velocity potential             & m^2/s & atm (ml+pl) & 131, 132           ",
-    "     151  & slp       & mean sea level pressure        & Pa    & surface     & 129, 130, 152       ",
-    "     156  & geopoth   & geopotential height            & m     & atm (ml+pl) & 129, 130, 133, 152 ",
-    "     157  & rhumidity & relative humidity              &       & atm (ml+pl) & 130, 133, 152      ",
-    "     189  & sclfs     & surface solar cloud forcing    &       & surface     & 176-185            ",
-    "     190  & tclfs     & surface thermal cloud forcing  &       & surface     & 177-186            ",
-    "     191  & sclf0     & top solar cloud forcing        &       & surface     & 178-187             ",
-    "     192  & tclf0     & top thermal cloud forcing      &       & surface     & 179-188            ",
-    "     259  & windspeed & windspeed                      & m/s   & atm (ml+pl) & sqrt(u*u+v*v)      ",
-    "     260  & precip    & total precipitation            &       & surface     & 142+143            ",
-    "    ",
-    "    LEVEL selects the hybrid or pressure levels. The allowed values depends on the parameter TYPE.",
-    "    The default value -1 processes all detected levels.",
-    "    ",
-    "    INTERVAL selects the processing interval. The default value 0 process data on monthly intervals.",
-    "    INTERVAL=1 sets the interval to daily.",
-    "    ",
-    "    MEAN=1 compute and write monthly or daily mean fields. The default value 0 writes out all timesteps.",
-    "    ",
-    "    EXTRAPOLATE=0 switch of the extrapolation of missing values during the interpolation from model to pressure",
-    "    level (only available with MEAN=0 and TYPE=30). The default value 1 extrapolate missing values.",
-    "    ",
-    "    Possible combinations of TYPE, CODE and MEAN:",
-    "    ",
-    "          TYPE   & CODE                    & MEAN",
-    "        0/10/11  & 130  temperature        &  0",
-    "        0/10/11  & 131  u-velocity         &  0",
-    "        0/10/11  & 132  v-velocity         &  0",
-    "        0/10/11  & 133  specific humidity  &  0",
-    "        0/10/11  & 138  vorticity          &  0",
-    "        0/10/11  & 148  streamfunction     &  0",
-    "        0/10/11  & 149  velocity potential &  0",
-    "        0/10/11  & 152  LnPs               &  0",
-    "        0/10/11  & 155  divergence         &  0",
-    "         >11     & all codes               &  0/1",
-    "",
-    "PARAMETER",
-    "    vct  STRING  File with VCT in ASCII format",
-    nullptr
-};
-
-static const char *FilterHelp[] = {
-    "NAME",
-    "    bandpass, lowpass, highpass - Time series filtering",
-    "",
-    "SYNOPSIS",
-    "    bandpass,fmin,fmax  infile outfile",
-    "    lowpass,fmax  infile outfile",
-    "    highpass,fmin  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module takes the time series for each gridpoint in infile and (fast fourier) transforms it ",
-    "    into the frequency domain. According to the particular operator and its parameters certain frequencies ",
-    "    are filtered (set to zero) in the frequency domain and the spectrum is (inverse fast fourier) transformed ",
-    "    back into the time domain.",
-    "    To determine the frequency the time-axis of infile is used. (Data should have a constant time increment ",
-    "    since this assumption applies for transformation. However, the time increment has to be different from zero.)",
-    "    All frequencies given as parameter are interpreted per year. This is done by the assumption of a 365-day calendar. ",
-    "    Consequently if you want to perform multiyear-filtering accurately you have to delete the 29th of February. ",
-    "    If your infile has a 360 year calendar the frequency parameters fmin respectively fmax should be ",
-    "    multiplied with a factor of 360/365 in order to obtain accurate results.  ",
-    "    For the set up of a frequency filter the frequency parameters have to be adjusted to a frequency in the data. ",
-    "    Here fmin is rounded down and fmax is always rounded up. Consequently it is possible to use bandpass with ",
-    "    fmin=fmax without getting a zero-field for outfile. ",
-    "    Hints for efficient usage: ",
-    "    - to get reliable results the time-series has to be detrended (cdo detrend)",
-    "    - the lowest frequency greater zero that can be contained in infile is 1/(N*dT), ",
-    "    - the greatest frequency is 1/(2dT) (Nyquist frequency),",
-    "    with N the number of timesteps and dT the time increment of infile in years.",
-    "    ",
-    "    Missing value support for operators in this module is not implemented, yet!",
-    "",
-    "OPERATORS",
-    "    bandpass  Bandpass filtering",
-    "              Bandpass filtering (pass for frequencies between fmin and fmax).",
-    "              Suppresses all variability outside the frequency range specified by [fmin,fmax].",
-    "    lowpass   Lowpass filtering",
-    "              Lowpass filtering (pass for frequencies lower than fmax).",
-    "              Suppresses all variability with frequencies greater than fmax. ",
-    "    highpass  Highpass filtering",
-    "              Highpass filtering (pass for frequencies greater than fmin). ",
-    "              Suppresses all variabilty with frequencies lower than fmin. ",
-    "",
-    "PARAMETER",
-    "    fmin  FLOAT	Minimum frequency per year that passes the filter.",
-    "    fmax  FLOAT	Maximum frequency per year that passes the filter.  ",
-    "",
-    "NOTE",
-    "    For better performace of these operators use the CDO configure option --with-fftw3.",
-    nullptr
-};
-
-static const char *GridcellHelp[] = {
-    "NAME",
-    "    gridarea, gridweights - Grid cell quantities",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module reads the grid cell area of the first grid from the input stream. If the grid cell area is missing it",
-    "    will be computed from the grid coordinates. The area of a grid cell is calculated using spherical triangles from",
-    "    the coordinates of the center and the vertices. The base is a unit sphere which is scaled with the radius of the earth.",
-    "    The default earth radius is 6371000 meter. This value can be changed with the environment variable PLANET_RADIUS.",
-    "    Depending on the chosen operator the grid cell area or weights are written to the output stream.",
-    "",
-    "OPERATORS",
-    "    gridarea     Grid cell area",
-    "                 Writes the grid cell area to the output stream. If the grid cell area have to",
-    "                 be computed it is scaled with the earth radius to square meters.",
-    "    gridweights  Grid cell weights",
-    "                 Writes the grid cell area weights to the output stream.",
-    "",
-    "ENVIRONMENT",
-    "    PLANET_RADIUS",
-    "        This variable is used to scale the computed grid cell areas to square meters. ",
-    "        By default PLANET_RADIUS is set to an earth radius of 6371000 meter.",
-    nullptr
-};
-
-static const char *SmoothHelp[] = {
-    "NAME",
-    "    smooth, smooth9 - Smooth grid points",
-    "",
-    "SYNOPSIS",
-    "    smooth[,options]  infile outfile",
-    "    smooth9  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Smooth all grid points of a horizontal grid.",
-    "    Options is a comma-separated list of \"key=value\" pairs with optional parameters.",
-    "",
-    "OPERATORS",
-    "    smooth   Smooth grid points",
-    "             Performs a N point smoothing on all input fields. The number of points used depend",
-    "             on the search radius (radius) and the maximum number of points (maxpoints).",
-    "             Per default all points within the search radius of 1degree are used.",
-    "             The weights for the points depend on the form of the curve and the distance.",
-    "             The implemented form of the curve is linear with constant default weights of 0.25",
-    "             at distance 0 (weight0) and at the search radius (weightR).",
-    "    smooth9  9 point smoothing",
-    "             Performs a 9 point smoothing on all fields with a quadrilateral curvilinear grid.",
-    "             The result at each grid point is a weighted average of the grid point plus",
-    "             the 8 surrounding points. The center point receives a weight of 1.0, the ",
-    "             points at each side and above and below receive a weight of 0.5, and corner ",
-    "             points receive a weight of 0.3.",
-    "             All 9 points are multiplied by their weights and summed, then divided by ",
-    "             the total weight to obtain the smoothed value. Any missing data points are ",
-    "             not included in the sum; points beyond the grid boundary are considered to ",
-    "             be missing. Thus the final result may be the result of an averaging with less ",
-    "             than 9 points.",
-    "",
-    "PARAMETER",
-    "    nsmooth    INTEGER  Number of times to smooth, default nsmooth=1",
-    "    radius     STRING   Search radius, default radius=1deg (units: deg, rad, km, m)",
-    "    maxpoints  INTEGER  Maximum number of points, default maxpoints=<gridsize>",
-    "    form       STRING   Form of the curve, default form=linear",
-    "    weight0    FLOAT    Weight at distance 0, default weight0=0.25",
-    "    weightR    FLOAT    Weight at the search radius, default weightR=0.25",
-    nullptr
-};
-
-static const char *DeltatHelp[] = {
-    "NAME",
-    "    deltat - Difference between timesteps",
-    "",
-    "SYNOPSIS",
-    "    deltat  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator computes the difference between each timestep.",
-    nullptr
-};
-
-static const char *ReplacevaluesHelp[] = {
-    "NAME",
-    "    setvals, setrtoc, setrtoc2 - Replace variable values",
-    "",
-    "SYNOPSIS",
-    "    setvals,oldval,newval[,...]  infile outfile",
-    "    setrtoc,rmin,rmax,c  infile outfile",
-    "    setrtoc2,rmin,rmax,c,c2  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module replaces old variable values with new values, depending on the operator.",
-    "",
-    "OPERATORS",
-    "    setvals   Set list of old values to new values",
-    "              Supply a list of n pairs of old and new values.",
-    "    setrtoc   Set range to constant",
-    "                       / c      if i(t,x) GE rmin AND i(t,x) LE rmax",
-    "              o(t,x) = ",
-    "                       \\ i(t,x) if i(t,x) LT rmin AND i(t,x) GT rmax",
-    "    setrtoc2  Set range to constant others to constant2",
-    "                       / c      if i(t,x) GE rmin AND i(t,x) LE rmax",
-    "              o(t,x) = ",
-    "                       \\ c2     if i(t,x) LT rmin AND i(t,x) GT rmax",
-    "",
-    "PARAMETER",
-    "    oldval,newval,...  FLOAT   Pairs of old and new values",
-    "    rmin               FLOAT   Lower bound",
-    "    rmax               FLOAT   Upper bound",
-    "    c                  FLOAT   New value - inside range",
-    "    c2                 FLOAT   New value - outside range",
-    nullptr
-};
-
-static const char *GetgridcellHelp[] = {
-    "NAME",
-    "    gridcellindex - Get grid cell index",
-    "",
-    "SYNOPSIS",
-    "    gridcellindex[,parameter]  infile",
-    "",
-    "DESCRIPTION",
-    "    Get the grid cell index of one grid point selected by the parameter lon and lat.",
-    "",
-    "PARAMETER",
-    "    lon  INTEGER   Longitude of the grid cell in degree",
-    "    lat  INTEGER   Latitude of the grid cell in degree",
-    nullptr
-};
-
-static const char *VargenHelp[] = {
-    "NAME",
-    "    const, random, topo, seq, stdatm - Generate a field",
-    "",
-    "SYNOPSIS",
-    "    const,const,grid  outfile",
-    "    random,grid[,seed]  outfile",
-    "    topo[,grid]  outfile",
-    "    seq,start,end[,inc]  outfile",
-    "    stdatm,levels  outfile",
-    "",
-    "DESCRIPTION",
-    "    Generates a dataset with one or more fields",
-    "",
-    "OPERATORS",
-    "    const   Create a constant field",
-    "            Creates a constant field. All field elements of the grid have the same value.",
-    "    random  Create a field with random numbers",
-    "            Creates a field with rectangularly distrubuted random numbers in the interval [0,1].",
-    "    topo    Create a field with topography",
-    "            Creates a field with topography data, per default on a global half degree grid.",
-    "    seq     Create a time series",
-    "            Creates a time series with field size 1 and field elements beginning with a start value in time step 1",
-    "            which is increased from one time step to the next.",
-    "    stdatm  Create values for pressure and temperature for hydrostatic atmosphere",
-    "            Creates pressure and temperature values for the given list of vertical levels.",
-    "            The formulars are:",
-    "            ",
-    "            P(z) = P_0 * exp(-1 * g/R * H/T_0 * log( (exp(z/H)*T_0 + T_Delta)/(T_0 + T_Delta))",
-    "            T(z) = T_0 + T_Delta * exp(-z/H)",
-    "            ",
-    "            with the following constants",
-    "            ",
-    "            T_0     = 213 K           Offset to get a surface temperature of 288K",
-    "            T_Delta = 75 K            Temperature lapse rate for 10Km",
-    "            P_0     = 1013.25 hPa     Surface pressure",
-    "            H       = 10000.0 m       Scale height",
-    "            g       = 9.80665 m/s**2  Earth gravity",
-    "            R       = 287.05 J/kg*K   Gas constant for air",
-    "            ",
-    "            This is the solution for the hydrostatic equations and is only valid for the",
-    "            troposphere (constant positive lapse rate). The temperature increase in the",
-    "            stratosphere and other effects of the upper atmosphere are not taken into",
-    "            account.",
-    "",
-    "PARAMETER",
-    "    const   FLOAT   Constant",
-    "    seed    INTEGER The seed for a new sequence of pseudo-random numbers [default: 1]",
-    "    grid    STRING  Target grid description file or name",
-    "    start   FLOAT   Start value of the loop",
-    "    end     FLOAT   End value of the loop",
-    "    inc     FLOAT   Increment of the loop [default: 1]",
-    "    levels  FLOAT   Target levels in metre above surface",
-    nullptr
-};
-
-static const char *TimsortHelp[] = {
-    "NAME",
-    "    timsort - Timsort",
-    "",
-    "SYNOPSIS",
-    "    timsort  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Sorts the elements in ascending order over all timesteps for every field position.",
-    "    After sorting it is:",
-    "    ",
-    "    o(t_1,x) <= o(t_2,x)      forall (t_1<t_2),x",
-    nullptr
-};
-
-static const char *WindTransHelp[] = {
-    "NAME",
-    "    uvDestag, rotuvNorth, projuvLatLon - Wind Transformation",
-    "",
-    "SYNOPSIS",
-    "    uvDestag,u,v[,-/+0.5[,-/+0.5]]  infile outfile",
-    "    rotuvNorth,u,v  infile outfile",
-    "    projuvLatLon,u,v  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains special operators for datsets with wind components on a rotated lon/lat grid, ",
-    "    e.g. data from the regional model HIRLAM or REMO. ",
-    "",
-    "OPERATORS",
-    "    uvDestag      Destaggering of u/v wind components",
-    "                  This is a special operator for destaggering of wind components.",
-    "                  If the file contains a grid with temperature (name='t' or code=11)",
-    "                  then grid_temp will be used for destaggered wind.",
-    "    rotuvNorth    Rotate u/v wind to North pole.",
-    "                  This is an operator for transformation of wind-vectors from grid-relative to north-pole",
-    "                  relative for the whole file. (FAST implementation with JACOBIANS)",
-    "    projuvLatLon  Cylindrical Equidistant projection",
-    "                  Thus is an operator for transformation of wind-vectors from the globe-spherical coordinate system",
-    "                  into a flat Cylindrical Equidistant (lat-lon) projection. (FAST JACOBIAN implementation)",
-    "",
-    "PARAMETER",
-    "    u,v            STRING  Pair of u,v wind components (use variable names or code numbers)",
-    "    -/+0.5,-/+0.5  STRING  Destaggered grid offsets are optional (default -0.5,-0.5)",
-    nullptr
-};
-
-static const char *RotuvbHelp[] = {
-    "NAME",
-    "    rotuvb - Rotation",
-    "",
-    "SYNOPSIS",
-    "    rotuvb,u,v,...  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This is a special operator for datsets with wind components on a rotated grid, ",
-    "    e.g. data from the regional model REMO. It performs a backward transformation of ",
-    "    velocity components U and V from a rotated spherical system to a geographical system.",
-    "",
-    "PARAMETER",
-    "    u,v,...  STRING  Pairs of zonal and meridional velocity components (use variable names or code numbers)",
-    "",
-    "NOTE",
-    "    This is a specific implementation for data from the REMO model, it may not work with data from other sources.",
-    nullptr
-};
-
-static const char *MrotuvbHelp[] = {
-    "NAME",
-    "    mrotuvb - Backward rotation of MPIOM data",
-    "",
-    "SYNOPSIS",
-    "    mrotuvb  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    MPIOM data are on a rotated Arakawa C grid. The velocity components U and V are located on",
-    "    the edges of the cells and point in the direction of the grid lines and rows.",
-    "    With mrotuvb the velocity vector is rotated in latitudinal and longitudinal direction.",
-    "    Before the rotation, U and V are interpolated to the scalar points (cell center).",
-    "    U is located with the coordinates for U in infile1 and V in infile2.",
-    "    mrotuvb assumes a positive meridional flow for a flow from grid point(i,j) to grid point(i,j+1)",
-    "    and positive zonal flow for a flow from grid point(i+1,j) to point(i,j).",
-    "",
-    "NOTE",
-    "    This is a specific implementation for data from the MPIOM model, it may not work with data from other sources.",
-    nullptr
-};
-
-static const char *MastrfuHelp[] = {
-    "NAME",
-    "    mastrfu - Mass stream function",
-    "",
-    "SYNOPSIS",
-    "    mastrfu  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This is a special operator for the post processing of the atmospheric general circulation",
-    "    model ECHAM. It computes the mass stream function (code=272). The input dataset have ",
-    "    to be a zonal mean of v-velocity [m/s] (code=132) on pressure levels.",
-    nullptr
-};
-
-static const char *DeriveparHelp[] = {
-    "NAME",
-    "    sealevelpressure, gheight - Derived model parameters",
-    "",
-    "SYNOPSIS",
-    "    <operator>  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains operators that calculate derived model parameters. These are currently the parameters",
-    "    sea level pressure and geopotential height. All necessary input parameters are identified by their GRIB1",
-    "    code number or the NetCDF CF standard name.",
-    "    Supported GRIB1 parameter tables are: WMO standard table number 2 and ECMWF local table number 128.",
-    "    ",
-    "     CF standard name            & Units      & GRIB 1 code      ",
-    "       surface_air_pressure      &  Pa        &  134",
-    "       air_temperature           &  K         &  130",
-    "       specific_humidity         &  kg/kg     &  133",
-    "       surface_geopotential      &  m2 s-2    &  129",
-    "       geopotential_height       &  m         &  156",
-    "    ",
-    "",
-    "OPERATORS",
-    "    sealevelpressure  Sea level pressure",
-    "                      This operator computes the sea level pressure (air_pressure_at_sea_level). Required input fields",
-    "                      are surface_air_pressure, surface_geopotential and air_temperature on full hybrid sigma pressure levels.",
-    "    gheight           Geopotential height",
-    "                      This operator computes the geopotential height (geopotential_height) on full model levels in metres.",
-    "                      Required input fields are surface_air_pressure, surface_geopotential, specific_humidity and air_temperature",
-    "                      on full hybrid sigma pressure levels. Note, this procedure is an approximation, which doesn't take into",
-    "                      account the effects of e.g. cloud ice and water, rain and snow.",
-    nullptr
-};
-
-static const char *AdisitHelp[] = {
-    "NAME",
-    "    adisit, adipot - Potential temperature to in-situ temperature and vice versa",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,pressure]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "",
-    "OPERATORS",
-    "    adisit  Potential temperature to in-situ temperature",
-    "            This is a special operator for the post processing of the ocean and sea ice model MPIOM.",
-    "            It converts potential temperature adiabatically to in-situ temperature to(t, s, p).",
-    "            Required input fields are sea water potential temperature (name=tho; code=2) and sea water salinity (name=sao; code=5).",
-    "            Pressure is calculated from the level information or can be specified by the optional parameter.",
-    "            Output fields are sea water temperature (name=to; code=20) and sea water salinity (name=s; code=5).",
-    "    adipot  In-situ temperature to potential temperature",
-    "            This is a special operator for the post processing of the ocean and sea ice model MPIOM.",
-    "            It converts in-situ temperature to potential temperature tho(to, s, p). Required input fields",
-    "            are sea water in-situ temperature (name=t; code=2) and sea water salinity (name=sao,s; code=5).",
-    "            Pressure is calculated from the level information or can be specified by the optional parameter.",
-    "            Output fields are sea water temperature (name=tho; code=2) and sea water salinity (name=s; code=5).",
-    "",
-    "PARAMETER",
-    "    pressure  FLOAT   Pressure in bar (constant value assigned to all levels)",
-    nullptr
-};
-
-static const char *RhopotHelp[] = {
-    "NAME",
-    "    rhopot - Calculates potential density",
-    "",
-    "SYNOPSIS",
-    "    rhopot[,pressure]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This is a special operator for the post processing of the ocean and sea ice model MPIOM.",
-    "    It calculates the sea water potential density (name=rhopoto; code=18). Required input fields ",
-    "    are sea water in-situ temperature (name=to; code=20) and sea water salinity (name=sao; code=5).",
-    "    Pressure is calculated from the level information or can be specified by the optional parameter.",
-    "",
-    "PARAMETER",
-    "    pressure  FLOAT   Pressure in bar (constant value assigned to all levels)",
-    nullptr
-};
-
-static const char *HistogramHelp[] = {
-    "NAME",
-    "    histcount, histsum, histmean, histfreq - Histogram",
-    "",
-    "SYNOPSIS",
-    "    <operator>,bounds  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module creates bins for a histogram of the input data.",
-    "    The bins have to be adjacent and have non-overlapping intervals.",
-    "    The user has to define the bounds of the bins. The first value",
-    "    is the lower bound and the second value the upper bound of the",
-    "    first bin. The bounds of the second bin are defined by the",
-    "    second and third value, aso.",
-    "    Only 2-dimensional input fields are allowed. The output file ",
-    "    contains one vertical level for each of the bins requested.",
-    "",
-    "OPERATORS",
-    "    histcount  Histogram count",
-    "               Number of elements in the bin range.",
-    "    histsum    Histogram sum",
-    "               Sum of elements in the bin range.",
-    "    histmean   Histogram mean",
-    "               Mean of elements in the bin range.",
-    "    histfreq   Histogram frequency",
-    "               Relative frequency of elements in the bin range.",
-    "",
-    "PARAMETER",
-    "    bounds  FLOAT  Comma-separated list of the bin bounds (-inf and inf valid)",
-    nullptr
-};
-
-static const char *SethaloHelp[] = {
-    "NAME",
-    "    sethalo - Set the bounds of a field",
-    "",
-    "SYNOPSIS",
-    "    sethalo[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator sets the boundary in the east, west, south and north of the rectangular understood fields.",
-    "    Positive values of the parameters increase the boundary in the selected direction. Negative values",
-    "    decrease the field at the selected boundary. The new rows and columns are filled with the missing value.",
-    "    With the optional parameter value a different fill value can be used. Global cyclic fields are filled",
-    "    cyclically at the east and west borders, if the fill value is not set by the user.",
-    "",
-    "PARAMETER",
-    "    east   INTEGER  East halo",
-    "    west   INTEGER  West halo",
-    "    south  INTEGER  South halo",
-    "    north  INTEGER  North halo",
-    "    value  FLOAT    Fill value (default is the missing value)",
-    nullptr
-};
-
-static const char *WctHelp[] = {
-    "NAME",
-    "    wct - Windchill temperature",
-    "",
-    "SYNOPSIS",
-    "    wct  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 and infile2 be time series of temperature and wind",
-    "    speed records, then a corresponding time series of resulting windchill",
-    "    temperatures is written to outfile. The wind chill temperature",
-    "    calculation is only valid for a temperature of T <= 33 °C and a wind speed",
-    "    of v >= 1.39 m/s. Whenever these conditions are not satisfied, a missing",
-    "    value is written to outfile. Note that temperature and wind speed records",
-    "    have to be given in units of °C and m/s, respectively.",
-    nullptr
-};
-
-static const char *FdnsHelp[] = {
-    "NAME",
-    "    fdns - Frost days where no snow index per time period",
-    "",
-    "SYNOPSIS",
-    "    fdns  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily minimum temperature TN",
-    "    and infile2 be a corresponding series of daily surface snow",
-    "    amounts. Then the number of days where TN < 0 °C and the surface ",
-    "    snow amount is less than 1 cm is counted. The temperature TN",
-    "    have to be given in units of Kelvin.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile.",
-    nullptr
-};
-
-static const char *StrwinHelp[] = {
-    "NAME",
-    "    strwin - Strong wind days index per time period",
-    "",
-    "SYNOPSIS",
-    "    strwin[,v]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum horizontal wind speed",
-    "    VX, then the number of days where VX > v is counted. The horizontal wind",
-    "    speed v is an optional parameter with default v = 10.5 m/s. A further",
-    "    output variable is the maximum number of consecutive days with maximum wind",
-    "    speed greater than or equal to v. Note that both VX and v have to be given in",
-    "    units of m/s. Also note that the horizontal wind speed is defined as the",
-    "    square root of the sum of squares of the zonal and meridional wind speeds.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    v  FLOAT   Horizontal wind speed threshold (m/s, default v = 10.5 m/s)",
-    nullptr
-};
-
-static const char *StrbreHelp[] = {
-    "NAME",
-    "    strbre - Strong breeze days index per time period",
-    "",
-    "SYNOPSIS",
-    "    strbre  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum horizontal wind speed",
-    "    VX, then the number of days where VX is greater than or equal to 10.5 m/s ",
-    "    is counted. A further output variable is the maximum number of consecutive",
-    "    days with maximum wind speed greater than or equal to 10.5 m/s. Note that",
-    "    VX is defined as the square root of the sum of squares of the zonal and",
-    "    meridional wind speeds and have to be given in units of m/s.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile.",
-    nullptr
-};
-
-static const char *StrgalHelp[] = {
-    "NAME",
-    "    strgal - Strong gale days index per time period",
-    "",
-    "SYNOPSIS",
-    "    strgal  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum horizontal wind speed",
-    "    VX, then the number of days where VX is greater than or equal to 20.5 m/s ",
-    "    is counted. A further output variable is the maximum number of consecutive",
-    "    days with maximum wind speed greater than or equal to 20.5 m/s. Note that",
-    "    VX is defined as the square root of the sum of square of the zonal and",
-    "    meridional wind speeds and have to be given in units of m/s.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile.",
-    nullptr
-};
-
-static const char *HurrHelp[] = {
-    "NAME",
-    "    hurr - Hurricane days index per time period",
-    "",
-    "SYNOPSIS",
-    "    hurr  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum horizontal wind speed",
-    "    VX, then the number of days where VX is greater than or equal to 32.5 m/s",
-    "    is counted. A further output variable is the maximum number of consecutive",
-    "    days with maximum wind speed greater than or equal to 32.5 m/s. Note that",
-    "    VX is defined as the square root of the sum of squares of the zonal and",
-    "    meridional wind speeds and have to be given in units of m/s.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile.",
-    nullptr
-};
-
-static const char *CMORliteHelp[] = {
-    "NAME",
-    "    cmorlite - CMOR lite",
-    "",
-    "SYNOPSIS",
-    "    cmorlite,table[,convert]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    The CMOR (Climate Model Output Rewriter) library comprises a set of",
-    "    functions, that can be used to produce CF-compliant NetCDF files that ",
-    "    fulfill the requirements of many of the climate community's standard",
-    "    model experiments. These experiments are collectively referred to as",
-    "    MIP's. Much of the metadata written to the output files is defined in",
-    "    MIP-specific tables, typically made available from each MIP's web site.",
-    "    ",
-    "    The CDO operator cmorlite process the header and variable section",
-    "    of such MIP tables and writes the result with the internal IO library CDI.",
-    "    In addition to the CMOR 2 and 3 table format, the CDO parameter table format",
-    "    is also supported. The following parameter table entries are available:",
-    "    ",
-    "     Entry           & Type        & Description      ",
-    "     name            & WORD        & Name of the variable",
-    "     out_name        & WORD        & New name of the variable",
-    "     type            & WORD        & Data type (real or double)",
-    "     standard_name   & WORD        & As defined in the CF standard name table",
-    "     long_name       & STRING      & Describing the variable",
-    "     units           & STRING      & Specifying the units for the variable",
-    "     comment         & STRING      & Information concerning the variable",
-    "     cell_methods    & STRING      & Information concerning calculation of means or climatologies",
-    "     cell_measures   & STRING      & Indicates the names of the variables containing cell areas and volumes",
-    "     missing_value   & FLOAT       & Specifying how missing data will be identified",
-    "     valid_min       & FLOAT       & Minimum valid value",
-    "     valid_max       & FLOAT       & Maximum valid value",
-    "     ok_min_mean_abs & FLOAT       & Minimum absolute mean",
-    "     ok_max_mean_abs & FLOAT       & Maximum absolute mean",
-    "     factor          & FLOAT       & Scale factor",
-    "     delete          & INTEGER     & Set to 1 to delete variable",
-    "     convert         & INTEGER     & Set to 1 to convert the unit if necessary",
-    "    ",
-    "    Most of the above entries are stored as variables attributes, some of them are handled differently.",
-    "    The variable name is used as a search key for the parameter table. valid_min, valid_max,",
-    "    ok_min_mean_abs and ok_max_mean_abs are used to check the range of the data.",
-    "",
-    "PARAMETER",
-    "    table    STRING   Name of the CMOR table as specified from PCMDI",
-    "    convert  STRING   Converts the units if necessary",
-    nullptr
-};
-
-static const char *VerifygridHelp[] = {
-    "NAME",
-    "    verifygrid - Verify grid coordinates",
-    "",
-    "SYNOPSIS",
-    "    verifygrid  infile",
-    "",
-    "DESCRIPTION",
-    "    This operator verifies the coordinates of all horizontal grids found in infile.",
-    "    Among other things, it searches for duplicate cells, non-convex cells,",
-    "    and whether the center is located outside the cell bounds.",
-    "    Use the CDO option -v to output the position of these cells.",
-    "    This information can be useful to avoid problems when interpolating the data.",
-    nullptr
-};
-
-static const char *HealpixHelp[] = {
-    "NAME",
-    "    hpdegrade, hpupgrade - Change healpix resolution",
-    "",
-    "SYNOPSIS",
-    "    <operator>,parameter  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Degrade or upgrade the resolution of a healpix grid.",
-    "",
-    "OPERATORS",
-    "    hpdegrade  Degrade healpix",
-    "               Degrade the resolution of a healpix grid. The value of the target pixel is the mean of the source pixels.",
-    "    hpupgrade  Upgrade healpix",
-    "               Upgrade the resolution of a healpix grid. The values of the target pixels is the value of the source pixel.",
-    "",
-    "PARAMETER",
-    "    nside  INTEGER The nside of the target healpix, must be a power of two [default: same as input].",
-    "    order  STRING  Pixel ordering of the target healpix ('nested' or 'ring').",
-    "    power  FLOAT   If non-zero, divide the result by (nside[in]/nside[out])**power. power=-2 keeps the sum of the map invariant.",
-    nullptr
-};
-
-static const char *NCL_windHelp[] = {
-    "NAME",
-    "    uv2vr_cfd, uv2dv_cfd - Wind transformation",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,u,v,boundOpt,outMode]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    This module contains CDO operators with an interface to NCL functions.",
-    "    The corresponding NCL functions have the same name. A more detailed description",
-    "    of those NCL function can be found on the NCL homepage https://www.ncl.ucar.edu.",
-    "",
-    "OPERATORS",
-    "    uv2vr_cfd  U and V wind to relative vorticity",
-    "               Computes relative vorticity for a latitude-longitude grid using centered finite differences.",
-    "               The grid need not be global and missing values are allowed.",
-    "    uv2dv_cfd  U and V wind to divergence",
-    "               Computes divergence for a latitude-longitude grid using centered finite differences.",
-    "               The grid need not be global and missing values are allowed.",
-    "",
-    "PARAMETER",
-    "    u         STRING   Name of variable u (default: u)",
-    "    v         STRING   Name of variable v (default: v)",
-    "    boundOpt  INTEGER  Boundary condition option (0-3) (default: 0/1 for cyclic grids)",
-    "    outMode   STRING   Output mode new/append (default: new)",
-    nullptr
-};
-
-static const char *CMORHelp[] = {
-    "NAME",
-    "    cmor - Climate Model Output Rewriting to produce CMIP-compliant data",
-    "",
-    "SYNOPSIS",
-    "    cmor,MIPtable[,cmor_name=VarList[,key=value[,...]]]  infile",
-    "",
-    "DESCRIPTION",
-    "    ",
-    "    ",
-    "    The CDO operator cmor converts an infile into a CMIP-compliant format",
-    "    by using the CMOR library. Each output file contains a single output variable.",
-    "    The name of the output files are generated by CMOR according to a template based on the",
-    "    DRS (Data reference Syntax) of the project. CMOR checks and applies the information delivered",
-    "    through the project dependend MIPtable on the infile. Additional information",
-    "    which is required for the conversion can be configured via keyvalues as optional parameters.",
-    "    ",
-    "    By specifying a variable selector keyvalue, e.g. cmor_name=tas, the user can",
-    "    pre-select a subset of infile variables. If name or code is specified, a",
-    "    corresponding cmor_name which can also be found in the MIPtable is also",
-    "    required to map the infile variable to the CMOR-variable. For mapping more",
-    "    variables at the operator call, one can specify a mapping table via keyword mapping_table.",
-    "    ",
-    "    Global attributes must be collected in info files and can be specified via keyword",
-    "    info. All required and optional global attributes as well as information",
-    "    about table file formats are given in the 'cdo cmor manual'.",
-    "    ",
-    "    If questions remain, do not hesitate to ask and send an email to wachsmannATdkrz.de.",
-    "    ",
-    "",
-    "PARAMETER",
-    "    MIPtable                   STRING    Name of the MIP table as used by CMOR.",
-    "                               --------------------------------------------------------------------------------------------",
-    "    cmor_name           | cn   STRING    Variable selector and specified in the MIP table.",
-    "                                         Comma-separated list of CMOR variable names.",
-    "                                         Default is to process all variables.",
-    "    name                | n    STRING    Variable selector.",
-    "                                         Name of a selected @file{infile} variable.",
-    "    code                | c    INTEGER   Variable selector. ",
-    "                                         Three digits (GRIB) Code of a selected @file{infile} variable.",
-    "                               --------------------------------------------------------------------------------------------",
-    "    info                | i    STRING    Preprozessing.",
-    "                                         Comma-separated list of filenames.",
-    "                                         Containins global attributes and control keywords.",
-    "                                         Default: CWD/.cdocmorinfo",
-    "    grid_info           | gi   STRING    Preprozessing.",
-    "                                         NetCDF or table formatted file with model grid description.",
-    "                                         Horizontal and vertical axes are substituted with the ones from grid info file.",
-    "    mapping_table       | mt   STRING    Preprozessing.",
-    "                                         Fortran Namelist containing variable information for e.g. renaming.",
-    "    keep_all_attributes | kaa  STRING    Preprozessing.",
-    "                                         'y' for passing all infile attributes. 'n' for discarding all infile attributes.",
-    "                               --------------------------------------------------------------------------------------------",
-    "    drs                 | d    CHARACTER Output control.",
-    "                                         Do(=y, default) or do not(=n) move output into the project DRS structure.",
-    "    drs_root            | dr   STRING    Output control. CMOR output root directory.",
-    "                                         Default: CWD.",
-    "    output_mode         | om   CHARACTER Output control.",
-    "                                         Either 'r' for replace (default) or 'a' for append mode.",
-    "    last_chunk          | lc   STRING    Output control. Filename of chunk to which shall be appended.  ",
-    "    max_size            | ms   INTEGER   Output control. Limit of output file sie in GigaByte.",
-    "    deflate_level       | dl   INTEGER   Output control. Compression level. -1: No compression. 0: Only shuffle.",
-    "    version_date        | vd   INTEGER   Output control. Subdirectory name in CMIP6 DRS.",
-    "                               --------------------------------------------------------------------------------------------",
-    "    required_time_units | rtu  STRING    Temporal description.",
-    "                                         Time axis reference date specified by the experiment.",
-    "                                         Format: 'days since YYYY-day-month hh:mm:ss'.",
-    "    cell_methods        | cm   CHARACTER Temporal description.",
-    "                                         Cell_methods of time axis.",
-    "                                         Value is one of 'm' (default)  , 'p', 'c', 'n', 'd'",
-    "                               --------------------------------------------------------------------------------------------",
-    "    units               | u    STRING    Variable attrbiute. Units of the variable.",
-    "                                         Must be known by library UDunits.",
-    "    variable_comment    | vc   STRING    Variable attribute. Variable comment.",
-    "    positive            | p    CHARACTER Variable attrbiute.",
-    "                                         Positive flux direction, either 'u' for upward or 'd' for downward.",
-    "    z_axis              | za   STRING    Name of the coordinate variable associated with",
-    "                                         the z-axis of the target variable.",
-    "    character_axis      | ca   STRING    Name of the coordinate variable associated with",
-    "                                         a character axis of the target variable.",
-    "                                         Valid axes names are: basin, vegtype or oline. ",
-    "    t_axis              | ta   STRING    Sets time values and time bounds to the nearest value",
-    "                                         required by the project given by the value of t_axis.",
-    "                                         Valid value is: cmip",
-    nullptr
-};
-
-static const char *MagplotHelp[] = {
-    "NAME",
-    "    contour, shaded, grfill - Lat/Lon plot",
-    "",
-    "SYNOPSIS",
-    "    <operator>,parameter  infile obase",
-    "",
-    "DESCRIPTION",
-    "    The operators in this module generates 2D Lon/Lat plots.",
-    "    The data for the plot is read from infile.",
-    "    Only data on rectilinear Lon/Lat grids are supported.",
-    "    The output file will be named <obase>_<param>.<device> where param is the parameter name and",
-    "    device is the device name. The default output file format is postscript,",
-    "    this can be changed with the device parameter.",
-    "    The type of the plot depends on the choosen operator.",
-    "    ",
-    "    Here is a list of all common plot parameters:",
-    "    ",
-    "     Keyname     & Type    & Description      ",
-    "     device      & STRING  & Output device (ps, eps, pdf, png, gif, gif_animation, jpeg, svg, kml)",
-    "     projection  & STRING  & Projection (cylindrical, polar_stereographic, robinson, mercator)",
-    "     style       & STRING  & Contour line style (solid, dash, dot, chain_dash, chain_dot)",
-    "     min         & FLOAT   & Minimum value",
-    "     max         & FLOAT   & Maximum value",
-    "     lon_max     & FLOAT   & Maximum longitude of the image",
-    "     lon_min     & FLOAT   & Minimum longitude of the image",
-    "     lat_max     & FLOAT   & Maximum latitude of the image",
-    "     lat_min     & FLOAT   & Minimum latitude of the image",
-    "     count       & INTEGER & Number of Contour levels / Colour bands  ",
-    "     interval    & FLOAT   & Interval in data units between two bands lines",
-    "     list        & INTEGER & List of levels to be plotted",
-    "     RGB         & STRING  & TRUE or FALSE, to  indicate, if the input colour is in RGB format",
-    "     step_freq   & INTEGER & Frequency of time steps to be considered for making the animation",
-    "                 &         & (device=gif_animation). Default value is \"1\" (all time steps).",
-    "                 &         & Will be ignored if input file has multiple variables.",
-    "     file_split  & STRING  & TRUE or FALSE, to split the output file for each variable, if input has",
-    "                 &         & multiple variables. Default value is \"FALSE\". Valid only for \"PS\" format.",
-    "",
-    "OPERATORS",
-    "    contour  Contour plot",
-    "             The operator contour generates the discrete contour lines of the input field values.",
-    "             The following additional parameters are valid for contour operator,",
-    "             module in addition to the common plot parameters:",
-    "             ",
-    "              Keyname      & Type    & Description      ",
-    "              colour       & STRING  & Colour for drawing the contours",
-    "              thickness    & FLOAT   & Thickness of the contour line",
-    "              style        & STRING  & Line Style can be \"SOLID\", \"DASH\", \"DOT\", \"CHAIN_DASH\",",
-    "                           &         & \"CHAIN_DOT\"",
-    "    shaded   Shaded contour plot",
-    "             The operator shaded generates the filled contours of the given input field values.",
-    "             The following additional parameters are valid for shaded contour and gridfill operator,",
-    "             in addition to the common plot parameters.",
-    "             ",
-    "              Keyname      & Type    & Description      ",
-    "              colour_min   & STRING  & Colour for the Minimum colour band",
-    "              colour_max   & STRING  & Colour for the Minimum colour band",
-    "              colour_triad & STRING  & Direction of colour sequencing for shading \"CW\" or \"ACW\",",
-    "                           &         & to denote \"clockwise\" and \"anticlockwise\" respectively.",
-    "                           &         & To be used in conjunction with \"colour_min\", \"colour_max\"",
-    "                           &         & options. Default is \"ACW\"",
-    "              colour_table & STRING  & File with user specified colours with the format as",
-    "             ",
-    "             Example file for 6 colours in RGB format:",
-    "             	6",
-    "             	RGB(0.0;0.0;1.0)",
-    "             	RGB(0.0;0.0;0.5)",
-    "             	RGB(0.0;0.5;0.5)",
-    "             	RGB(0.0;1.0;0.0)",
-    "             	RGB(0.5;0.5;0.0)",
-    "             	RGB(1.0;0.0;0.0)",
-    "             ",
-    "    grfill   Shaded gridfill plot",
-    "             The operator grfill is similar to satellite imaging and shades each cell (pixel) according",
-    "             to the value of the field at that cell.",
-    "",
-    "PARAMETER",
-    "    parameter  STRING   Comma-separated list of plot parameters",
-    "",
-    "NOTE",
-    "    All colour parameter can be either standard name or in RGB format.",
-    "    The valid standard name strings for \"colour\" are:",
-    "    ",
-    "    \"red\", \"green\", \"blue\", \"yellow\", \"cyan\", \"magenta\", \"black\", \"avocado\", \"beige\",",
-    "    \"brick\", \"brown\", \"burgundy\", \"charcoal\", \"chestnut\", \"coral\", \"cream\", \"evergreen\",",
-    "    \"gold\", \"grey\", \"khaki\", \"kellygreen\", \"lavender\", \"mustard\", \"navy\", \"ochre\",",
-    "    \"olive\", \"peach\", \"pink\", \"rose\", \"rust\", \"sky\", \"tan\", \"tangerine\", \"turquoise\",",
-    "    \"violet\", \"reddishpurple\", \"purplered\", \"purplishred\", \"orangishred\", \"redorange\",",
-    "    \"reddishorange\", \"orange\", \"yellowishorange\", \"orangeyellow\", \"orangishyellow\",",
-    "    \"greenishyellow\", \"yellowgreen\", \"yellowishgreen\", \"bluishgreen\", \"bluegreen\",",
-    "    \"greenishblue\", \"purplishblue\", \"bluepurple\", \"bluishpurple\", \"purple\", \"white\"",
-    nullptr
-};
-
-static const char *MagvectorHelp[] = {
-    "NAME",
-    "    vector - Lat/Lon vector plot",
-    "",
-    "SYNOPSIS",
-    "    vector,parameter  infile obase",
-    "",
-    "DESCRIPTION",
-    "    This operator generates 2D Lon/Lat vector plots.",
-    "    The data for the plot is read from infile. The input is expected to contain two velocity",
-    "    components. Only data on rectilinear Lon/Lat grids are supported.",
-    "    The output file will be named <obase>.<device> where device is the device name. ",
-    "    The default output file format is postscript, this can be changed with the device parameter.",
-    "    ",
-    "    Here is a list of all vector plot parameters:",
-    "    ",
-    "     Keyname     & Type    & Description      ",
-    "     device      & STRING  & Output device (ps, eps, pdf, png, gif, gif_animation, jpeg, svg, kml)",
-    "     projection  & STRING  & Projection (cylindrical, polar_stereographic, robinson, mercator)",
-    "     thin_fac    & FLOAT   & Controls the actual number of wind arrows or flags plotted (default 2).",
-    "     unit_vec    & FLOAT   & Wind speed in m/s represented by a unit vector (1.0cm)",
-    "     step_freq   & INTEGER & Frequency of time steps to be considered for making the animation",
-    "                 &         & (device=gif_animation). Default value is \"1\" (all time steps).",
-    "                 &         & Will be ignored if input file has multiple variables.",
-    "",
-    "PARAMETER",
-    "    parameter  STRING   Comma-separated list of plot parameters",
-    nullptr
-};
-
-static const char *MaggraphHelp[] = {
-    "NAME",
-    "    graph - Line graph plot",
-    "",
-    "SYNOPSIS",
-    "    graph,parameter  infiles outfile",
-    "",
-    "DESCRIPTION",
-    "    This operator generates line graph plots.",
-    "    The data for the plot is read from infiles. The result is written to outfile.",
-    "    The default output file format is postscript, this can be changed with the device parameter.",
-    "    ",
-    "    Here is a list of all graph plot parameters:",
-    "    ",
-    "     Keyname    & Type    & Description      ",
-    "     device     & STRING  & Output device (ps, eps, pdf, png, gif, gif_animation, jpeg, svg, kml)",
-    "     ymin       & FLOAT   & Minimum value of the y-axis data ",
-    "     ymax       & FLOAT   & Maximum value of the y-axis data ",
-    "     linewidth  & INT     & Linewidth (default 8)",
-    "     stat       & STRING  & \"TRUE\" or \"FALSE\", to switch on the mean computation. Default is \"FALSE\".",
-    "                &         & Will be overridden to \"FALSE\", if input files have unequal number of time",
-    "                &         & steps or different start/end times. ",
-    "     sigma      & FLOAT   & Standard deviation value for generating shaded back ground around the mean value.",
-    "                &         & To be used in conjunction with 'stat=\"TRUE\"' ",
-    "     obsv       & STRING  & To indicate if the input files have an observation data, by setting to \"TRUE\".",
-    "                &         & Default value is \"FALSE\". The observation data should be the first file in the",
-    "                &         & input file list. The observation data is always plotted in black colour. ",
-    "",
-    "PARAMETER",
-    "    parameter  STRING   Comma-separated list of plot parameters",
-    nullptr
-};
-
-static const char *EcaCddHelp[] = {
-    "NAME",
-    "    eca_cdd, etccdi_cdd - Consecutive dry days index per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,R[,N[,params]]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily precipitation amount RR, then the largest number ",
-    "    of consecutive days where RR is less than R is counted. R is an optional parameter with ",
-    "    default R = 1 mm. A further output variable is the number of dry periods of more than N days.",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_cdd     Consecutive dry days index per time period",
-    "                The operator counts over the entire time series.",
-    "                The date information of a timestep in outfile is the date of",
-    "                the last contributing timestep in infile.",
-    "    etccdi_cdd  Consecutive dry days index per time period",
-    "                The default output frequency is yearly.",
-    "                Periods within overlapping years are accounted for the first year.",
-    "                The date information of a timestep in outfile is the mid of",
-    "                the frequency interval.",
-    "",
-    "PARAMETER",
-    "    R     FLOAT    Precipitation threshold (unit: mm; default: R = 1 mm)",
-    "    N     INTEGER  Minimum number of days exceeded (default: N = 5)",
-    "    freq  STRING   Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaCfdHelp[] = {
-    "NAME",
-    "    eca_cfd - Consecutive frost days index per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_cfd[,N]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily minimum temperature TN, then the largest number of",
-    "    consecutive days where TN < 0 °C is counted. Note that TN have to be given in units of Kelvin.",
-    "    A further output variable is the number of frost periods of more than N days.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    N  INTEGER  Minimum number of days exceeded (default: N = 5)",
-    nullptr
-};
-
-static const char *EcaCsuHelp[] = {
-    "NAME",
-    "    eca_csu - Consecutive summer days index per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_csu[,T[,N]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum temperature TX, then the largest number of consecutive",
-    "    days where TX > T is counted. The number T is an optional parameter with default T = 25°C.",
-    "    Note that TN have to be given in units of Kelvin, whereas T have to be given in degrees Celsius.",
-    "    A further output variable is the number of summer periods of more than N days.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    T  FLOAT    Temperature threshold (unit: °C; default: T = 25°C)",
-    "    N  INTEGER  Minimum number of days exceeded (default: N = 5)",
-    nullptr
-};
-
-static const char *EcaCwdHelp[] = {
-    "NAME",
-    "    eca_cwd, etccdi_cwd - Consecutive wet days index per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_cwd[,R[,N[,params]]]  infile outfile",
-    "    etccdi_cwd  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily precipitation amount RR, then the largest number ",
-    "    of consecutive days where RR is at least R is counted. R is an optional parameter with ",
-    "    default R = 1 mm. A further output variable is the number of wet periods of more than N days.",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_cwd     Consecutive wet days index per time period",
-    "                The operator counts over the entire time series.",
-    "                The date information of a timestep in outfile is the date of",
-    "                the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    R     FLOAT    Precipitation threshold (unit: mm; default: R = 1 mm)",
-    "    N     INTEGER  Minimum number of days exceeded (default: N = 5)",
-    "    freq  STRING   Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaCwdiHelp[] = {
-    "NAME",
-    "    eca_cwdi - Cold wave duration index wrt mean of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_cwdi[,nday[,T]]  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily minimum temperature TN, and let infile2 be the mean ",
-    "    TNnorm of daily minimum temperatures for any period used as reference. Then counted is the number of days",
-    "    where, in intervals of at least nday consecutive days, TN < TNnorm - T.",
-    "    The numbers nday and T are optional parameters with default nday = 6 and T = 5°C. ",
-    "    A further output variable is the number of cold waves longer than or equal to nday days.",
-    "    TNnorm is calculated as the mean of minimum temperatures of a five day window centred on each calendar day ",
-    "    of a given climate reference period. Note that both TN and TNnorm have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    "",
-    "PARAMETER",
-    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
-    "    T     FLOAT    Temperature offset (unit: °C; default: T = 5°C)",
-    nullptr
-};
-
-static const char *EcaCwfiHelp[] = {
-    "NAME",
-    "    eca_cwfi, etccdi_csdi - ",
-    "    Cold-spell days index wrt 10th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,nday[,params]]  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily mean temperature TG, and infile2 be the 10th",
-    "    percentile TGn10 of daily mean temperatures for any period used as reference. ",
-    "    Then counted is the number of days where, in intervals of at least nday consecutive days,",
-    "    TG < TGn10. The number nday is an optional parameter with default nday = 6.",
-    "    A further output variable is the number of cold-spell periods longer than or equal to nday days.",
-    "    TGn10 is calculated as the 10th percentile of daily mean temperatures of a five day window ",
-    "    centred on each calendar day of a given climate reference period. Note that both TG and TGn10 ",
-    "    have to be given in the same units.",
-    "",
-    "OPERATORS",
-    "    eca_cwfi     Cold-spell days index wrt 10th percentile of reference period",
-    "                 The operator counts over the entire time series.",
-    "                 The date information of a timestep in outfile is the date of",
-    "                 the last contributing timestep in infile.",
-    "    etccdi_csdi  Cold-spell duration index",
-    "                 The default output frequency is yearly.",
-    "                 Periods within overlapping years are accounted for the first year.",
-    "                 The date information of a timestep in outfile is the mid of",
-    "                 the frequency interval.",
-    "",
-    "PARAMETER",
-    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
-    "    freq  STRING   Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaEtrHelp[] = {
-    "NAME",
-    "    eca_etr - Intra-period extreme temperature range",
-    "",
-    "SYNOPSIS",
-    "    eca_etr  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 and infile2 be time series of thr maximum and minimum",
-    "    temperature TX and TN, respectively. Then the extreme temperature",
-    "    range is the difference of the maximum of TX and the minimum of TN.",
-    "    Note that TX and TN have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timesteps in infile1 and infile2.",
-    nullptr
-};
-
-static const char *EcaFdHelp[] = {
-    "NAME",
-    "    eca_fd, etccdi_fd - Frost days index per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily minimum temperature TN,",
-    "    then the number of days where TN < 0 °C is counted. Note",
-    "    that TN have to be given in units of Kelvin. Parameter is a",
-    "    comma-separated list of \"key=value\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_fd     Frost days index per time period",
-    "               The operator counts over the entire time series.",
-    "               The date information of a timestep in outfile is the date of",
-    "               the last contributing timestep in infile.",
-    "    etccdi_fd  Frost days index per time period",
-    "               The default output frequency is yearly.",
-    "               The date information of a timestep in outfile is the mid of",
-    "               the frequency interval.",
-    "",
-    "PARAMETER",
-    "    freq  STRING    Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaGslHelp[] = {
-    "NAME",
-    "    eca_gsl - Thermal Growing season length index",
-    "",
-    "SYNOPSIS",
-    "    eca_gsl[,nday[,T[,fland]]]  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily mean temperature TG, and infile2 be a land-water mask.",
-    "    Within a period of 12 months, the thermal growing season length is officially defined as the number of days between:",
-    "    - first occurrence of at least nday consecutive days with TG > T",
-    "    - first occurrence of at least nday consecutive days with TG < T within the last 6 months",
-    "    On northern hemisphere, this period corresponds with the regular year, whereas on southern hemisphere, it starts ",
-    "    at July 1st. Please note, that this definition may lead to weird results concerning values TG = T: ",
-    "    In the first half of the period, these days do not contribute to the gsl, but they do within the second half.",
-    "    Moreover this definition could lead to discontinuous values in equatorial regions.",
-    "    ",
-    "    The numbers nday and T are optional parameter with default nday = 6 and T = 5°C. ",
-    "    The number fland is an optional parameter with default value fland = 0.5 and denotes the fraction of ",
-    "    a grid point that have to be covered by land in order to be included in the calculation. A further output variable ",
-    "    is the start day of year of the growing season. Note that TG have to be given in units of Kelvin, whereas T ",
-    "    have to be given in degrees Celsius.",
-    "    ",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    nday   INTEGER  Number of consecutive days (default: nday = 6)",
-    "    T      FLOAT    Temperature threshold (unit: °C; default: T = 5°C)",
-    "    fland  FLOAT    Land fraction threshold (default: fland = 0.5)",
-    nullptr
-};
-
-static const char *EcaHdHelp[] = {
-    "NAME",
-    "    eca_hd - Heating degree days per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_hd[,T1[,T2]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily mean temperature TG, then the heating degree days ",
-    "    are defined as the sum of T1 - TG, where only values TG < T2 are considered. ",
-    "    If T1 and T2 are omitted, a temperature of 17°C is used for both parameters. ",
-    "    If only T1 is given, T2 is set to T1. Note that TG have to be given in units ",
-    "    of kelvin, whereas T1 and T2 have to be given in degrees Celsius.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    T1  FLOAT   Temperature limit (unit: °C; default: T1 = 17°C)",
-    "    T2  FLOAT   Temperature limit (unit: °C; default: T2 = T1)",
-    nullptr
-};
-
-static const char *EcaHwdiHelp[] = {
-    "NAME",
-    "    eca_hwdi - Heat wave duration index wrt mean of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_hwdi[,nday[,T]]  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily maximum temperature TX, and let infile2 be the mean ",
-    "    TXnorm of daily maximum temperatures for any period used as reference. Then counted is the number of days",
-    "    where, in intervals of at least nday consecutive days, TX > TXnorm + T.",
-    "    The numbers nday and T are optional parameters with default nday = 6 and T = 5°C. ",
-    "    A further output variable is the number of heat waves longer than or equal to nday days. ",
-    "    TXnorm is calculated as the mean of maximum temperatures of a five day window centred on each calendar day",
-    "    of a given climate reference period. Note that both TX and TXnorm have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    "",
-    "PARAMETER",
-    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
-    "    T     FLOAT    Temperature offset (unit: °C; default: T = 5°C)",
-    nullptr
-};
-
-static const char *EcaHwfiHelp[] = {
-    "NAME",
-    "    eca_hwfi, etccdi_wsdi - ",
-    "    Warm spell days index wrt 90th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_hwfi[,nday[,params]]  infile1 infile2 outfile",
-    "    etccdi_wsdi  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily mean temperature TG, and ",
-    "    infile2 be the 90th percentile TGn90 of daily mean temperatures",
-    "    for any period used as reference. Then counted is the number of days",
-    "    where, in intervals of at least nday consecutive days, TG > TGn90. The",
-    "    number nday is an optional parameter with default nday = 6. A further",
-    "    output variable is the number of warm-spell periods longer than or",
-    "    equal to nday days. ",
-    "    TGn90 is calculated as the 90th percentile of daily mean temperatures of a five ",
-    "    day window centred on each calendar day of a given climate reference period.",
-    "    Note that both TG and TGn90 have to be given in the same units.",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_hwfi     Warm spell days index wrt 90th percentile of reference period",
-    "                 The operator counts over the entire time series.",
-    "                 The date information of a timestep in outfile is the date of",
-    "                 the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    nday  INTEGER  Number of consecutive days (default: nday = 6)",
-    "    freq  STRING   Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaIdHelp[] = {
-    "NAME",
-    "    eca_id, etccdi_id - Ice days index per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum temperature TX,",
-    "    then the number of days where TX < 0 °C is counted. Note",
-    "    that TX have to be given in units of Kelvin. Parameter is a",
-    "    comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_id     Ice days index per time period",
-    "               The operator counts over the entire time series.",
-    "               The date information of a timestep in outfile is the date of",
-    "               the last contributing timestep in infile.",
-    "    etccdi_id  Ice days index per time period",
-    "               The default output frequency is yearly.",
-    "               The date information of a timestep in outfile is the mid of",
-    "               the frequency interval.",
-    "",
-    "PARAMETER",
-    "    freq  STRING    Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaR75pHelp[] = {
-    "NAME",
-    "    eca_r75p - Moderate wet days wrt 75th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_r75p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 75th percentile RRn75 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the percentage of wet days with RR > RRn75 is calculated. ",
-    "    RRn75 is calculated as the 75th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,75.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR75ptotHelp[] = {
-    "NAME",
-    "    eca_r75ptot - Precipitation percent due to R75p days",
-    "",
-    "SYNOPSIS",
-    "    eca_r75ptot  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 75th percentile RRn75 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn75 to the total ",
-    "    precipitation sum is calculated. ",
-    "    RRn75 is calculated as the 75th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,75.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR90pHelp[] = {
-    "NAME",
-    "    eca_r90p - Wet days wrt 90th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_r90p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 90th percentile RRn90 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the percentage of wet days with RR > RRn90 is calculated. ",
-    "    RRn90 is calculated as the 90th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,90.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR90ptotHelp[] = {
-    "NAME",
-    "    eca_r90ptot - Precipitation percent due to R90p days",
-    "",
-    "SYNOPSIS",
-    "    eca_r90ptot  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 90th percentile RRn90 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn90 to the total ",
-    "    precipitation sum is calculated. ",
-    "    RRn90 is calculated as the 90th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,90.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR95pHelp[] = {
-    "NAME",
-    "    eca_r95p - Very wet days wrt 95th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_r95p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 95th percentile RRn95 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the percentage of wet days with RR > RRn95 is calculated. ",
-    "    RRn95 is calculated as the 95th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,95.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR95ptotHelp[] = {
-    "NAME",
-    "    eca_r95ptot - Precipitation percent due to R95p days",
-    "",
-    "SYNOPSIS",
-    "    eca_r95ptot  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 95th percentile RRn95 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn95 to the total ",
-    "    precipitation sum is calculated. ",
-    "    RRn95 is calculated as the 95th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,95.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR99pHelp[] = {
-    "NAME",
-    "    eca_r99p - Extremely wet days wrt 99th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_r99p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 99th percentile RRn99 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the percentage of wet days with RR > RRn99 is calculated. ",
-    "    RRn99 is calculated as the 99th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,99.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaR99ptotHelp[] = {
-    "NAME",
-    "    eca_r99ptot - Precipitation percent due to R99p days",
-    "",
-    "SYNOPSIS",
-    "    eca_r99ptot  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series RR of the daily precipitation amount at wet days (precipitation >= 1 mm)",
-    "    and infile2 be the 99th percentile RRn99 of the daily precipitation amount at wet days for any period ",
-    "    used as reference. Then the ratio of the precipitation sum at wet days with RR > RRn99 to the total ",
-    "    precipitation sum is calculated. ",
-    "    RRn99 is calculated as the 99th percentile of all wet days of a given climate reference period.",
-    "    Usually infile2 is generated by the operator ydaypctl,99.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaPdHelp[] = {
-    "NAME",
-    "    eca_pd, eca_r10mm, eca_r20mm, etccdi_r1mm - ",
-    "    Precipitation days index per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_pd,x  infile outfile",
-    "    eca_r10mm  infile outfile",
-    "    eca_r20mm  infile outfile",
-    "    etccdi_r1mm[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily precipitation amount RR in [mm] (or alternatively in [kg m-2]),",
-    "    then the number of days where RR is at least x mm is counted. ",
-    "    eca_r10mm and eca_r20mm are specific ECA operators with a daily precipitation amount of 10 and 20 mm respectively.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile",
-    "    except for the etccdi operator. Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_pd       Precipitation days index per time period",
-    "                 Generic ECA operator with daily precipitation sum exceeding x mm.",
-    "    eca_r10mm    Heavy precipitation days index per time period",
-    "                 Specific ECA operator with daily precipitation sum exceeding 10 mm.",
-    "    eca_r20mm    Very heavy precipitation days index per time period",
-    "                 Specific ECA operator with daily precipitation sum exceeding 20 mm.",
-    "    etccdi_r1mm  Precipitation days index per time period",
-    "                 The default output frequency is yearly.",
-    "                 The date information of a timestep in outfile is the mid of",
-    "                 the frequency interval.",
-    "",
-    "PARAMETER",
-    "    x     FLOAT   Daily precipitation amount threshold in [mm]",
-    "    freq  STRING  Output frequency (year, month)",
-    "",
-    "NOTE",
-    "    Precipitation rates in [mm/s] have to be converted to precipitation amounts (multiply with 86400 s).",
-    "    Apart from metadata information the result of eca_pd,1 and eca_rr1 is the same.",
-    nullptr
-};
-
-static const char *EcaRr1Help[] = {
-    "NAME",
-    "    eca_rr1 - Wet days index per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_rr1[,R]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily precipitation amount RR in [mm] (or alternatively in [kg m-2]), then",
-    "    the number of days where RR is at least R is counted. R is an optional parameter with default R = 1 mm. ",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    R  FLOAT   Precipitation threshold (unit: mm; default: R = 1 mm)",
-    nullptr
-};
-
-static const char *EcaRx1dayHelp[] = {
-    "NAME",
-    "    eca_rx1day, etccdi_rx1day - ",
-    "    Highest one day precipitation amount per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,parameter]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily precipitation amount RR,",
-    "    then the maximum of RR is written to outfile. If the optional",
-    "    parameter mode is set to 'm' the maximum daily precipitation",
-    "    amounts are determined for each month. ",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_rx1day     Highest one day precipitation amount per time period",
-    "                   The operator counts over the entire time series.",
-    "                   The date information of a timestep in outfile is the date of",
-    "                   the last contributing timestep in infile.",
-    "    etccdi_rx1day  Maximum 1-day Precipitation",
-    "                   The default output frequency is yearly.",
-    "                   The date information of a timestep in outfile is the mid of",
-    "                   the frequency interval.",
-    "",
-    "PARAMETER",
-    "    freq  STRING  Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaRx5dayHelp[] = {
-    "NAME",
-    "    eca_rx5day, etccdi_rx5day - ",
-    "    Highest five-day precipitation amount per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,x[,params]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of 5-day precipitation totals RR, then the maximum of RR is written to outfile. ",
-    "    A further output variable is the number of 5 day period with precipitation totals greater than x mm, where x ",
-    "    is an optional parameter with default x = 50 mm.",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_rx5day     Highest five-day precipitation amount per time period",
-    "                   The operator counts over the entire time series.",
-    "                   The date information of a timestep in outfile is the date of",
-    "                   the last contributing timestep in infile.",
-    "    etccdi_rx5day  Highest five-day precipitation amount per time period",
-    "                   The default output frequency is yearly.",
-    "                   Periods within overlapping years are accounted for the first year.",
-    "                   The date information of a timestep in outfile is the mid of",
-    "                   the frequency interval.",
-    "",
-    "PARAMETER",
-    "    x     FLOAT   Precipitation threshold (unit: mm; default: x = 50 mm)",
-    "    freq  STRING  Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaSdiiHelp[] = {
-    "NAME",
-    "    eca_sdii - Simple daily intensity index per time period",
-    "",
-    "SYNOPSIS",
-    "    eca_sdii[,R]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily precipitation amount RR, then the mean precipitation amount at ",
-    "    wet days (RR >= R) is written to outfile. R is an optional parameter with default R = 1 mm.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile.",
-    "",
-    "PARAMETER",
-    "    R  FLOAT   Precipitation threshold (unit: mm; default: R = 1 mm)",
-    nullptr
-};
-
-static const char *EcaSuHelp[] = {
-    "NAME",
-    "    eca_su, etccdi_su - Summer days index per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,T[,params]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily maximum temperature TX, then the number of days where ",
-    "    TX > T is counted. The number T is an optional parameter with default T = 25°C. ",
-    "    Note that TX have to be given in units of Kelvin, whereas T have to be given in degrees Celsius.",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_su     Summer days index per time period",
-    "               The operator counts over the entire time series.",
-    "               The date information of a timestep in outfile is the date of",
-    "               the last contributing timestep in infile.",
-    "    etccdi_su  Summer days index per time period",
-    "               The default output frequency is yearly.",
-    "               The date information of a timestep in outfile is the mid of",
-    "               the frequency interval.",
-    "",
-    "PARAMETER",
-    "    T     FLOAT     Temperature threshold (unit: °C; default: T = 25°C)",
-    "    freq  STRING    Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaTg10pHelp[] = {
-    "NAME",
-    "    eca_tg10p - Cold days percent wrt 10th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_tg10p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily mean temperature TG, and",
-    "    infile2 be the 10th percentile TGn10 of daily mean temperatures",
-    "    for any period used as reference. Then the percentage of time where ",
-    "    TG < TGn10 is calculated.",
-    "    TGn10 is calculated as the 10th percentile of daily mean temperatures of a five ",
-    "    day window centred on each calendar day of a given climate reference period.",
-    "    Note that both TG and TGn10 have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaTg90pHelp[] = {
-    "NAME",
-    "    eca_tg90p - Warm days percent wrt 90th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_tg90p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily mean temperature TG, and",
-    "    infile2 be the 90th percentile TGn90 of daily mean temperatures",
-    "    for any period used as reference. Then the percentage of time where TG > TGn90 ",
-    "    is calculated. ",
-    "    TGn90 is calculated as the 90th percentile of daily mean temperatures of a five ",
-    "    day window centred on each calendar day of a given climate reference period.",
-    "    Note that both TG and TGn90 have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaTn10pHelp[] = {
-    "NAME",
-    "    eca_tn10p - Cold nights percent wrt 10th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_tn10p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time serie of the daily minimum temperature TN, and",
-    "    infile2 be the 10th percentile TNn10 of daily minimum temperatures",
-    "    for any period used as reference. Then the percentage of time where TN < TNn10 ",
-    "    is calculated.",
-    "    TNn10 is calculated as the 10th percentile of daily minimum temperatures of a five ",
-    "    day window centred on each calendar day of a given climate reference period.",
-    "    Note that both TN and TNn10 have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaTn90pHelp[] = {
-    "NAME",
-    "    eca_tn90p - Warm nights percent wrt 90th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_tn90p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily minimum temperature TN, and infile2 be the ",
-    "    90th percentile TNn90 of daily minimum temperatures for any period used as reference. ",
-    "    Then the percentage of time where TN > TNn90 is calculated. TNn90 is calculated as the 90th percentile",
-    "    of daily minimum temperatures of a five day window centred on each calendar day of a given climate",
-    "    reference period. Note that both TN and TNn90 have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaTrHelp[] = {
-    "NAME",
-    "    eca_tr, etccdi_tr - Tropical nights index per time period",
-    "",
-    "SYNOPSIS",
-    "    <operator>[,T[,params]]  infile outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile be a time series of the daily minimum temperature TN, then the number of days where ",
-    "    TN > T is counted. The number T is an optional parameter with default T = 20°C. ",
-    "    Note that TN have to be given in units of Kelvin, whereas T have to be given in degrees Celsius.",
-    "    Parameter is a comma-separated list of \"key=values\" pairs.",
-    "",
-    "OPERATORS",
-    "    eca_tr     Tropical nights index per time period",
-    "               The operator counts over the entire time series.",
-    "               The date information of a timestep in outfile is the date of",
-    "               the last contributing timestep in infile.",
-    "    etccdi_tr  Tropical nights index per time period",
-    "               The default output frequency is yearly.",
-    "               The date information of a timestep in outfile is the mid of",
-    "               the frequency interval.",
-    "",
-    "PARAMETER",
-    "    T     FLOAT   Temperature threshold (unit: °C; default: T = 20°C)",
-    "    freq  STRING  Output frequency (year, month)",
-    nullptr
-};
-
-static const char *EcaTx10pHelp[] = {
-    "NAME",
-    "    eca_tx10p - Very cold days percent wrt 10th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_tx10p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily maximum temperature TX, and",
-    "    infile2 be the 10th percentile TXn10 of daily maximum temperatures",
-    "    for any period used as reference. Then the percentage of time where TX < TXn10.",
-    "    is calculated.",
-    "    TXn10 is calculated as the 10th percentile of daily maximum temperatures of a five ",
-    "    day window centred on each calendar day of a given climate reference period.",
-    "    Note that both TX and TXn10 have to be givenin the same units.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaTx90pHelp[] = {
-    "NAME",
-    "    eca_tx90p - Very warm days percent wrt 90th percentile of reference period",
-    "",
-    "SYNOPSIS",
-    "    eca_tx90p  infile1 infile2 outfile",
-    "",
-    "DESCRIPTION",
-    "    Let infile1 be a time series of the daily maximum temperature TX, and",
-    "    infile2 be the 90th percentile TXn90 of daily maximum temperatures",
-    "    for any period used as reference. Then the percentage of time where TX > TXn90.",
-    "    is calculated.",
-    "    TXn90 is calculated as the 90th percentile of daily maximum temperatures of a five ",
-    "    day window centred on each calendar day of a given climate reference period.",
-    "    Note that both TX and TXn90 have to be given in the same units.",
-    "    The date information of a timestep in outfile is the date of",
-    "    the last contributing timestep in infile1.",
-    nullptr
-};
-
-static const char *EcaEtccdiHelp[] = {
-    "NAME",
-    "    etccdi_tx90p, etccdi_tx10p, etccdi_tn90p, etccdi_tn10p, etccdi_r95p, ",
-    "    etccdi_r99p - ",
-    "    ETCCDI conform index for a reference periode calculated with bootstrapping",
-    "",
-    "SYNOPSIS",
-    "    <operator>,n,startboot,endboot[,m]  infile1 infile2 infile3 outfile",
-    "",
-    "DESCRIPTION",
-    "    This module enables to compute Climate Extremes Indices according to the method recommended",
-    "    by the Expert Team on Climate Change Detection and Indices. It differs from the",
-    "    corresponding eca_* indices by applying bootstrapping for a reference period",
-    "    (see Zhang et al. 2005) given by startboot and endboot and using the R-type 8 method ",
-    "    for percentile calculation.",
-    "    A requirement for correct percentile calculation is that",
-    "    CDO_PCTL_NBINS>=window*(endboot-startboot+1)*(sizeof(double)/sizeof(int))+2",
-    "    This demands for high working storage since the entire data of the bootstrapping interval",
-    "    need to be hold in storage. Otherwise, a histogram is used to calculate the percentile.",
-    "    infile2 (infile3) contains the daily minimum (maximum) of the bootstrapping interval.",
-    "    If m=m, the output variable will be saved monthly, otherwise with yearly frequency.",
-    "",
-    "OPERATORS",
-    "    etccdi_tx90p  Percentage of Days when Daily Maximum Temperature is Above the 90th Percentile",
-    "    etccdi_tx10p  Percentage of Days when Daily Maximum Temperature is Below the 10th Percentile",
-    "    etccdi_tn90p  Percentage of Days when Daily Minimum Temperature is Above the 90th Percentile",
-    "    etccdi_tn10p  Percentage of Days when Daily Minimum Temperature is Below the 10th Percentile",
-    "    etccdi_r95p   Annual Total Precipitation when Daily Precipitation Exceeds the 95th Percentile of Wet Day Precipitation",
-    "    etccdi_r99p   Annual Total Precipitation when Daily Precipitation Exceeds the 99th Percentile of Wet Day Precipitation",
-    "",
-    "PARAMETER",
-    "    n          INTEGER  Window days, number of timesteps",
-    "    startboot  INTEGER  First year of bootstrapping interval",
-    "    endboot    INTEGER  Last year of bootstrapping interval",
-    "    m          CHARACTER Output frequency",
-    "",
-    "ENVIRONMENT",
-    "    CDO_PCTL_NBINS",
-    "        Sets the number of histogram bins. The default number is 101.",
-    nullptr
-};
-// clang-format on
+#include <vector>
+#include <string_view>
+
+typedef std::vector<std::string_view> CdoHelp;
+
+extern const CdoHelp InfoHelp;
+extern const CdoHelp SinfoHelp;
+extern const CdoHelp XSinfoHelp;
+extern const CdoHelp DiffHelp;
+extern const CdoHelp NinfoHelp;
+extern const CdoHelp ShowinfoHelp;
+extern const CdoHelp ShowattributeHelp;
+extern const CdoHelp FiledesHelp;
+extern const CdoHelp ApplyHelp;
+extern const CdoHelp CopyHelp;
+extern const CdoHelp TeeHelp;
+extern const CdoHelp PackHelp;
+extern const CdoHelp UnpackHelp;
+extern const CdoHelp BitroundingHelp;
+extern const CdoHelp ReplaceHelp;
+extern const CdoHelp DuplicateHelp;
+extern const CdoHelp MergegridHelp;
+extern const CdoHelp MergeHelp;
+extern const CdoHelp SplitHelp;
+extern const CdoHelp SplittimeHelp;
+extern const CdoHelp SplitselHelp;
+extern const CdoHelp SplitdateHelp;
+extern const CdoHelp DistgridHelp;
+extern const CdoHelp CollgridHelp;
+extern const CdoHelp SelectHelp;
+extern const CdoHelp SelmultiHelp;
+extern const CdoHelp SelvarHelp;
+extern const CdoHelp SeltimeHelp;
+extern const CdoHelp SelboxHelp;
+extern const CdoHelp SelregionHelp;
+extern const CdoHelp SelgridcellHelp;
+extern const CdoHelp SamplegridHelp;
+extern const CdoHelp SelyearidxHelp;
+extern const CdoHelp SelsurfaceHelp;
+extern const CdoHelp CondHelp;
+extern const CdoHelp Cond2Help;
+extern const CdoHelp CondcHelp;
+extern const CdoHelp MapReduceHelp;
+extern const CdoHelp CompHelp;
+extern const CdoHelp CompcHelp;
+extern const CdoHelp YmoncompHelp;
+extern const CdoHelp SetattributeHelp;
+extern const CdoHelp SetpartabHelp;
+extern const CdoHelp SetHelp;
+extern const CdoHelp SettimeHelp;
+extern const CdoHelp ChangeHelp;
+extern const CdoHelp SetgridHelp;
+extern const CdoHelp SetzaxisHelp;
+extern const CdoHelp InvertHelp;
+extern const CdoHelp InvertlevHelp;
+extern const CdoHelp ShiftxyHelp;
+extern const CdoHelp MaskregionHelp;
+extern const CdoHelp MaskboxHelp;
+extern const CdoHelp SetboxHelp;
+extern const CdoHelp EnlargeHelp;
+extern const CdoHelp SetmissHelp;
+extern const CdoHelp VertfillmissHelp;
+extern const CdoHelp TimfillmissHelp;
+extern const CdoHelp SetgridcellHelp;
+extern const CdoHelp ExprHelp;
+extern const CdoHelp MathHelp;
+extern const CdoHelp ArithcHelp;
+extern const CdoHelp ArithHelp;
+extern const CdoHelp DayarithHelp;
+extern const CdoHelp MonarithHelp;
+extern const CdoHelp YeararithHelp;
+extern const CdoHelp YhourarithHelp;
+extern const CdoHelp YdayarithHelp;
+extern const CdoHelp YmonarithHelp;
+extern const CdoHelp YseasarithHelp;
+extern const CdoHelp ArithdaysHelp;
+extern const CdoHelp ArithlatHelp;
+extern const CdoHelp TimcumsumHelp;
+extern const CdoHelp ConsecstatHelp;
+extern const CdoHelp VarsstatHelp;
+extern const CdoHelp EnsstatHelp;
+extern const CdoHelp Ensstat2Help;
+extern const CdoHelp EnsvalHelp;
+extern const CdoHelp FldstatHelp;
+extern const CdoHelp ZonstatHelp;
+extern const CdoHelp MerstatHelp;
+extern const CdoHelp GridboxstatHelp;
+extern const CdoHelp RemapstatHelp;
+extern const CdoHelp VertstatHelp;
+extern const CdoHelp TimselstatHelp;
+extern const CdoHelp TimselpctlHelp;
+extern const CdoHelp RunstatHelp;
+extern const CdoHelp RunpctlHelp;
+extern const CdoHelp TimstatHelp;
+extern const CdoHelp TimpctlHelp;
+extern const CdoHelp HourstatHelp;
+extern const CdoHelp HourpctlHelp;
+extern const CdoHelp DaystatHelp;
+extern const CdoHelp DaypctlHelp;
+extern const CdoHelp MonstatHelp;
+extern const CdoHelp MonpctlHelp;
+extern const CdoHelp YearmonstatHelp;
+extern const CdoHelp YearstatHelp;
+extern const CdoHelp YearpctlHelp;
+extern const CdoHelp SeasstatHelp;
+extern const CdoHelp SeaspctlHelp;
+extern const CdoHelp YhourstatHelp;
+extern const CdoHelp DhourstatHelp;
+extern const CdoHelp YdaystatHelp;
+extern const CdoHelp YdaypctlHelp;
+extern const CdoHelp YmonstatHelp;
+extern const CdoHelp YmonpctlHelp;
+extern const CdoHelp YseasstatHelp;
+extern const CdoHelp YseaspctlHelp;
+extern const CdoHelp YdrunstatHelp;
+extern const CdoHelp YdrunpctlHelp;
+extern const CdoHelp FldcorHelp;
+extern const CdoHelp TimcorHelp;
+extern const CdoHelp FldcovarHelp;
+extern const CdoHelp TimcovarHelp;
+extern const CdoHelp RegresHelp;
+extern const CdoHelp DetrendHelp;
+extern const CdoHelp TrendHelp;
+extern const CdoHelp TrendarithHelp;
+extern const CdoHelp EOFsHelp;
+extern const CdoHelp EofcoeffHelp;
+extern const CdoHelp RemapbilHelp;
+extern const CdoHelp RemapbicHelp;
+extern const CdoHelp RemapnnHelp;
+extern const CdoHelp RemapdisHelp;
+extern const CdoHelp RemapconHelp;
+extern const CdoHelp RemaplafHelp;
+extern const CdoHelp RemapHelp;
+extern const CdoHelp RemapetaHelp;
+extern const CdoHelp VertintmlHelp;
+extern const CdoHelp VertintapHelp;
+extern const CdoHelp VertintghHelp;
+extern const CdoHelp IntlevelHelp;
+extern const CdoHelp Intlevel3dHelp;
+extern const CdoHelp InttimeHelp;
+extern const CdoHelp IntyearHelp;
+extern const CdoHelp SpectralHelp;
+extern const CdoHelp SpecconvHelp;
+extern const CdoHelp Wind2Help;
+extern const CdoHelp WindHelp;
+extern const CdoHelp FourierHelp;
+extern const CdoHelp ImportbinaryHelp;
+extern const CdoHelp ImportcmsafHelp;
+extern const CdoHelp ImportamsrHelp;
+extern const CdoHelp InputHelp;
+extern const CdoHelp OutputHelp;
+extern const CdoHelp OutputtabHelp;
+extern const CdoHelp OutputgmtHelp;
+extern const CdoHelp GradsdesHelp;
+extern const CdoHelp AfterburnerHelp;
+extern const CdoHelp FilterHelp;
+extern const CdoHelp GridcellHelp;
+extern const CdoHelp SmoothHelp;
+extern const CdoHelp DeltatHelp;
+extern const CdoHelp ReplacevaluesHelp;
+extern const CdoHelp GetgridcellHelp;
+extern const CdoHelp VargenHelp;
+extern const CdoHelp TimsortHelp;
+extern const CdoHelp WindTransHelp;
+extern const CdoHelp RotuvbHelp;
+extern const CdoHelp MrotuvbHelp;
+extern const CdoHelp MastrfuHelp;
+extern const CdoHelp PressureHelp;
+extern const CdoHelp DeriveparHelp;
+extern const CdoHelp AdisitHelp;
+extern const CdoHelp RhopotHelp;
+extern const CdoHelp HistogramHelp;
+extern const CdoHelp SethaloHelp;
+extern const CdoHelp WctHelp;
+extern const CdoHelp FdnsHelp;
+extern const CdoHelp StrwinHelp;
+extern const CdoHelp StrbreHelp;
+extern const CdoHelp StrgalHelp;
+extern const CdoHelp HurrHelp;
+extern const CdoHelp CMORliteHelp;
+extern const CdoHelp VerifygridHelp;
+extern const CdoHelp HealpixHelp;
+extern const CdoHelp NCL_windHelp;
+extern const CdoHelp CMORHelp;
+extern const CdoHelp MagplotHelp;
+extern const CdoHelp MagvectorHelp;
+extern const CdoHelp MaggraphHelp;
+extern const CdoHelp EcaCddHelp;
+extern const CdoHelp EcaCfdHelp;
+extern const CdoHelp EcaCsuHelp;
+extern const CdoHelp EcaCwdHelp;
+extern const CdoHelp EcaCwdiHelp;
+extern const CdoHelp EcaCwfiHelp;
+extern const CdoHelp EcaEtrHelp;
+extern const CdoHelp EcaFdHelp;
+extern const CdoHelp EcaGslHelp;
+extern const CdoHelp EcaHdHelp;
+extern const CdoHelp EcaHwdiHelp;
+extern const CdoHelp EcaHwfiHelp;
+extern const CdoHelp EcaIdHelp;
+extern const CdoHelp EcaR75pHelp;
+extern const CdoHelp EcaR75ptotHelp;
+extern const CdoHelp EcaR90pHelp;
+extern const CdoHelp EcaR90ptotHelp;
+extern const CdoHelp EcaR95pHelp;
+extern const CdoHelp EcaR95ptotHelp;
+extern const CdoHelp EcaR99pHelp;
+extern const CdoHelp EcaR99ptotHelp;
+extern const CdoHelp EcaPdHelp;
+extern const CdoHelp EcaRr1Help;
+extern const CdoHelp EcaRx1dayHelp;
+extern const CdoHelp EcaRx5dayHelp;
+extern const CdoHelp EcaSdiiHelp;
+extern const CdoHelp EcaSuHelp;
+extern const CdoHelp EcaTg10pHelp;
+extern const CdoHelp EcaTg90pHelp;
+extern const CdoHelp EcaTn10pHelp;
+extern const CdoHelp EcaTn90pHelp;
+extern const CdoHelp EcaTrHelp;
+extern const CdoHelp EcaTx10pHelp;
+extern const CdoHelp EcaTx90pHelp;
+extern const CdoHelp EcaEtccdiHelp;
 
 #endif
diff --git a/src/par_io.cc b/src/par_io.cc
index f6003ee252b41f24d6ed91ebbad6af8bd092e8a2..028a3456a750524430d6e5d06c1fcda4b84f4a44 100644
--- a/src/par_io.cc
+++ b/src/par_io.cc
@@ -23,19 +23,19 @@ read_record(void *arg)
   auto streamID = read_arg->streamID;
   auto p_varID = read_arg->varID;
   auto p_levelID = read_arg->levelID;
-  auto p_nmiss = read_arg->nmiss;
+  auto p_numMissVals = read_arg->numMissVals;
   auto p_array = read_arg->array;
 
   // fprintf(stderr, "read_record: streamID = %d\n", streamID);
   cdo_inq_record(streamID, p_varID, p_levelID);
-  cdo_read_record(streamID, p_array, p_nmiss);
+  cdo_read_record(streamID, p_array, p_numMissVals);
   // fprintf(stderr, "read_record: varID %d levelID %d\n", *p_varID, *p_levelID);
 
   return nullptr;
 }
 
 void
-par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, size_t *nmiss, par_io_t *parIO)
+par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, size_t *numMissVals, par_io_t *parIO)
 {
   bool lpario = false;
   int recID = 0, nrecs = 0;
@@ -59,7 +59,7 @@ par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, s
       read_arg.streamID = streamID;
       read_arg.varID = varID;
       read_arg.levelID = levelID;
-      read_arg.nmiss = nmiss;
+      read_arg.numMissVals = numMissVals;
       read_arg.array = array;
 
       read_record(&read_arg);
@@ -73,7 +73,7 @@ par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, s
 
       *varID = parIO->varID;
       *levelID = parIO->levelID;
-      *nmiss = parIO->nmiss;
+      *numMissVals = parIO->numMissVals;
       // fprintf(stderr, "parIO2: %ld streamID %d %d %d\n", (long)thrID, streamID, *varID, *levelID);
       array_copy(parIO->array_size, parIO->array, array);
     }
@@ -92,7 +92,7 @@ par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, s
           read_arg->streamID = streamID;
           read_arg->varID = &parIO->varID;
           read_arg->levelID = &parIO->levelID;
-          read_arg->nmiss = &parIO->nmiss;
+          read_arg->numMissVals = &parIO->numMissVals;
           read_arg->array = parIO->array;
 
           // fprintf(stderr, "pthread_create: streamID %d %d\n", read_arg->streamID,streamID);
diff --git a/src/par_io.h b/src/par_io.h
index 3bd32f83f17e5d4d31c73d28cb60247aa3124a51..8a51b80cc6b719b882e7d23c56d9970698f56eea 100644
--- a/src/par_io.h
+++ b/src/par_io.h
@@ -10,19 +10,20 @@
 #endif
 
 #include <cstddef>
+#include "cdoStream.h"
 
 struct read_arg_t
 {
   CdoStreamID streamID;
   int *varID, *levelID;
-  size_t *nmiss;
+  size_t *numMissVals;
   double *array;
 };
 
 struct par_io_t
 {
   int varID, levelID;
-  size_t nmiss;
+  size_t numMissVals;
   double *array;
   int array_size;
   int recID, nrecs;
@@ -33,6 +34,6 @@ struct par_io_t
 #endif
 };
 
-void par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, size_t *nmiss, par_io_t *parIO);
+void par_read_record(CdoStreamID streamID, int *varID, int *levelID, double *array, size_t *numMissVals, par_io_t *parIO);
 
 #endif /* PAR_IO_H */
diff --git a/src/param_conversion.cc b/src/param_conversion.cc
index ec78adadd24bdca52369a966e896ce39931d58be..c648c90cf2ca832fb51a3633ea88b730c0e3741a 100644
--- a/src/param_conversion.cc
+++ b/src/param_conversion.cc
@@ -183,6 +183,25 @@ parameter_to_intlist(const std::string &string)
   return parameter_to_intlist(string.c_str());
 }
 
+double
+radius_str_to_meter(const std::string &string)
+{
+  char *endptr = nullptr;
+  auto radius = strtod(string.c_str(), &endptr);
+
+  if (*endptr != 0)
+    {
+      if (std::strncmp(endptr, "km", 2) == 0)
+        radius *= 1000;
+      else if (std::strncmp(endptr, "m", 1) == 0)
+        ;
+      else
+        cdo_abort("Float parameter >%s< contains invalid character at position %d!", string, (int) (endptr - string.c_str() + 1));
+    }
+
+  return radius;
+}
+
 double
 radius_str_to_deg(const std::string &string)
 {
@@ -192,9 +211,9 @@ radius_str_to_deg(const std::string &string)
   if (*endptr != 0)
     {
       if (std::strncmp(endptr, "km", 2) == 0)
-        radius = 360 * ((radius * 1000) / (2 * PlanetRadius * M_PI));
+        radius = 360 * ((radius * 1000) / (2.0 * PlanetRadiusDefault * M_PI));
       else if (std::strncmp(endptr, "m", 1) == 0)
-        radius = 360 * ((radius) / (2 * PlanetRadius * M_PI));
+        radius = 360 * ((radius) / (2.0 * PlanetRadiusDefault * M_PI));
       else if (std::strncmp(endptr, "deg", 3) == 0)
         ;
       else if (std::strncmp(endptr, "rad", 3) == 0)
@@ -223,22 +242,25 @@ string_to_param(const std::string &paramstr)
   return string_to_param(paramstr.c_str());
 }
 
-void
-param_to_string(int param, char *paramstr, int maxlen)
+std::string
+param_to_string(int param)
 {
+  char paramstr[CDI_MAX_NAME];
   int dis, cat, num;
   cdiDecodeParam(param, &num, &cat, &dis);
 
-  size_t umaxlen = (maxlen >= 0) ? (unsigned) maxlen : 0U;
+  int maxlen = sizeof(paramstr);
   int len;
   if (dis == 255 && (cat == 255 || cat == 0))
-    len = std::snprintf(paramstr, umaxlen, "%03d", num);
+    len = std::snprintf(paramstr, maxlen, "%03d", num);
   else if (dis == 255)
-    len = std::snprintf(paramstr, umaxlen, "%03d.%03d", num, cat);
+    len = std::snprintf(paramstr, maxlen, "%03d.%03d", num, cat);
   else
-    len = std::snprintf(paramstr, umaxlen, "%03d.%03d.%03d", num, cat, dis);
+    len = std::snprintf(paramstr, maxlen, "%03d.%03d.%03d", num, cat, dis);
 
   if (len >= maxlen || len < 0) cdo_abort("Internal problem (%s): size of input string is too small!", __func__);
+
+  return std::string{paramstr};
 }
 
 /* time/date/season converisons */
@@ -268,10 +290,7 @@ season_to_months(const char *season, int *imonths)
           size_t ke = ks + len;
           for (size_t k = ks; k < ke; ++k) imonths[imons[k]]++;
         }
-      else
-        {
-          cdo_abort("Season %s not available!", season);
-        }
+      else { cdo_abort("Season %s not available!", season); }
     }
 }
 
diff --git a/src/param_conversion.h b/src/param_conversion.h
index 662a0e528a8d5ef5ef9f2fb8a9b986597e130a8f..d4ff737678a93f15c5da1568ceb3e6c7f5eece8c 100644
--- a/src/param_conversion.h
+++ b/src/param_conversion.h
@@ -21,11 +21,11 @@ long parameter_to_long(const std::string &string);
 size_t parameter_to_size_t(const std::string &string);
 int parameter_to_intlist(const std::string &string);
 
+double radius_str_to_meter(const std::string &string);
 double radius_str_to_deg(const std::string &string);
 
 int string_to_param(const std::string &paramstr);
-int string_to_param(const char *paramstr);
-void param_to_string(int param, char *paramstr, int maxlen);
+std::string param_to_string(int param);
 
 /* time/date/season converisons */
 /* =================================================================================== */
diff --git a/src/parse_literals.cc b/src/parse_literals.cc
index a489519eaa685489cda0f2276f7067bf6c5d800e..3621ec12195cf923391735792f8451f17723c31b 100644
--- a/src/parse_literals.cc
+++ b/src/parse_literals.cc
@@ -64,10 +64,7 @@ literals_find_datatype(int n, const std::vector<std::string> &literals)
                   {
                     dtype = (dtype == CDI_DATATYPE_FLT32 || dtype == CDI_DATATYPE_FLT64) ? std::max(dtype, xtype) : xtype;
                   }
-                else
-                  {
-                    dtype = (!(dtype == CDI_DATATYPE_FLT32 || dtype == CDI_DATATYPE_FLT64)) ? std::max(dtype, xtype) : xtype;
-                  }
+                else { dtype = (!(dtype == CDI_DATATYPE_FLT32 || dtype == CDI_DATATYPE_FLT64)) ? std::max(dtype, xtype) : xtype; }
               }
           }
     }
diff --git a/src/parser.cc b/src/parser.cc
index 6d2732b1bcd9bd2c86ad6abe9491459381ac5c26..ed2eb17f247d8d9beaca00d2cbb6b1a2c72631d1 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -11,7 +11,7 @@
 #include "parser.h"
 #include "cdo_syntax_error.h"
 #include "cdo_node_attach_exception.h"
-#include "modules.h"
+#include "factory.h"
 #include "cdo_output.h"
 
 namespace Parser
@@ -28,6 +28,24 @@ static int staticParserCounter = 0;
 namespace Util
 {
 
+void
+extract_name_and_argument(const std::string &command, std::string &operatorName, std::string &operatorArgument)
+{
+  constexpr char delimiter = ',';
+
+  const size_t start = (command[0] == '-') ? 1 : 0;
+
+  size_t len = command.find(delimiter);
+  if (len == std::string::npos)
+    {
+      len = command.size();
+      operatorArgument = "";
+    }
+  else { operatorArgument = command.substr(len + 1, std::string::npos); }
+
+  operatorName = command.substr(start, len - start);
+}
+
 std::string
 tab()
 {
@@ -78,36 +96,79 @@ append(std::shared_ptr<T> &parent, std::shared_ptr<T> &child)
 }  // namespace Util
 
 // Factory Funcs!
+
 std::string
-err_msg_oper_not_found(const std::string &name)
+err_msg_oper_not_found(const std::string &operatorName)
 {
-  auto similarOperators = find_similar_operators(name);
-  if (similarOperators.size()) similarOperators = "\nSimilar operators: " + similarOperators;
-  return "Operator '" + name + "' not found" + similarOperators;
+  // Checking if the operatorname is an existing file name
+  auto fp = std::fopen(operatorName.c_str(), "r");
+
+// Sicnce std::format is incomplete, we sadly cannot use it yet
+// When the time comes: please remove the ifdef and keep the std::format variant
+#ifdef FINALLY_STD_FORMAT
+  std::string err_msg;
+
+  if (fp)
+    {
+      std::fclose(fp);
+      err_msg = std::format("Operator missing, {} is a file on disk!", operatorName);
+    }
+  else
+    {
+      // Operator is no filename
+      // Checking for similar operators
+      err_msg = std::format("Operator >{}< not found!\n"
+                            "Similar operators are:\n{}",
+                            operatorName.c_str(), Factory::find_similar_operators(operatorName));
+    }
+  return err_msg;
+#else
+  char err_msg[1024]{ 0 };
+  if (fp)
+    {
+      std::fclose(fp);
+      std::snprintf(err_msg, sizeof(err_msg), "Operator missing, %s is a file on disk!", operatorName.c_str());
+    }
+  else
+    {
+      // Operator is no filename
+      // Checking for similar operators
+      auto similar = Factory::find_similar_operators(operatorName);
+      std::snprintf(err_msg, sizeof(err_msg), "Operator >%s< not found!\nSimilar operators are:\n%s", operatorName.c_str(),
+                    similar.c_str());
+    }
+  return std::string(err_msg);
+#endif
 }
 
 // Factory Funcs!
 static std::shared_ptr<Node>
-create_operator_node(ARGV_ITERATOR &p_curentArgument)
+create_operator_node(ARGV_ITERATOR &p_curentArgument, bool is_first = false)
 {
   debug_parser("Creating new operator node: %s", *p_curentArgument);
 
   std::string operatorName = "";
   std::string operatorArguments = "";
 
-  extract_name_and_argument(*p_curentArgument, operatorName, operatorArguments);
+  Util::extract_name_and_argument(*p_curentArgument, operatorName, operatorArguments);
 
-  auto moduleIterator = find_module(operatorName);
+  auto moduleIterator = Factory::find(
+      operatorName, [&]() { THROW(InternalCdoSyntaxError, p_curentArgument, err_msg_oper_not_found(operatorName)); });
 
-  if (moduleIterator == get_modules().end())
+  auto mod = Factory::get_module(moduleIterator);
+  if (!is_first && mod.constraints.pos_restriction == PositionRestrictions::OnlyFirst)
     {
-      THROW(InternalCdoSyntaxError, p_curentArgument, err_msg_oper_not_found(operatorName));
+      THROW(InternalCdoSyntaxError, p_curentArgument, errmsg_not_in_first_position);
     }
-
-  auto mod = moduleIterator->second;
   auto newNode = std::make_shared<Node>(p_curentArgument, operatorName, operatorArguments, mod.constraints);
   return newNode;
 }
+static std::shared_ptr<Node>
+create_first_operator_node(ARGV_ITERATOR &p_curentArgument)
+{
+  return create_operator_node(p_curentArgument, true);
+}
+
 static std::shared_ptr<Node>
 create_file_node(ARGV_ITERATOR &p_curentArgument)
 {
@@ -164,10 +225,7 @@ public:
   {
     bool undecidable_from_here_on
         = !stack.empty() && (stack.back()->constraints.streamInCnt == -1) && (stack.back()->children.size() > 1);
-    if (cntVariableInputs > 1 && undecidable_from_here_on)
-      {
-        THROW(InternalCdoSyntaxError, node->iter, errmsg_multiple_variable);
-      }
+    if (cntVariableInputs > 1 && undecidable_from_here_on) { THROW(InternalCdoSyntaxError, node->iter, errmsg_multiple_variable); }
     if (node->constraints.streamInCnt == -1) { cntVariableInputs++; }
     debug_parser("pushing new node: %s", node->oper);
     stack.push_back(node);
@@ -247,7 +305,7 @@ handle_node(Parser &parser, ARGV_ITERATOR &cur_arg)
       std::shared_ptr<Node> &parent = parser.top();
       debug_parser("adding %s as leaf to %s", node->oper, parent->oper);
       Util::append(parent, node);
-      if(parent->is_done()) parser.pop();
+      if (parent->is_done()) parser.pop();
     }
   else
     {
@@ -431,18 +489,16 @@ iterate(PARSER_STACK &parser_stack, ARGV_ITERATOR &cur_arg, const ARGV_ITERATOR
 std::vector<std::shared_ptr<Node>>
 run(std::vector<std::string> &p_argv)
 {
-  ARGV_ITERATOR cur_arg;
-  ARGV_ITERATOR end;
   std::shared_ptr<Node> first_operator;
   std::stack<Parser> parser_stack;
 
-  cur_arg = p_argv.begin();
-  end = p_argv.end();
+  ARGV_ITERATOR cur_arg = p_argv.begin();
+  ARGV_ITERATOR end = p_argv.end();
 
   std::string first_argv = *cur_arg;
   if (first_argv.find("-apply,") == 0) { THROW(InternalCdoSyntaxError, cur_arg, errmsg_apply_in_first_pos); }
 
-  first_operator = create_operator_node(cur_arg);
+  first_operator = create_first_operator_node(cur_arg);
 
   // TODO remove abs replace with numOut < 0 : 1 else numOut
   int numOut = first_operator->numOut();
@@ -455,6 +511,7 @@ run(std::vector<std::string> &p_argv)
 
   parser_stack.push({ first_operator, cur_arg });
 
+  // MAIN LOOP
   iterate(parser_stack, cur_arg, without_out_files);
 
   if (parser_stack.size() > 1) { THROW(InternalCdoSyntaxError, parser_stack.top().start, errmsg_bracket_not_closed); }
diff --git a/src/parser.h b/src/parser.h
index 92bbe116f905dee93e19f4b8b79dbcc168ea3907..94e4d69f0e8813d89ecbb0ec385b87c18da47aaf 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -9,58 +9,60 @@
 namespace Parser
 {
 
-static std::string apply_help =
-  "        This feature allows to prepend simple cdo chains to other chains\n"
-  "        Apply syntax:\n"
-  "            (1) [ chain : file1 file2 file ]             (Recommended Syntax)\n"
-  "            (2) -apply,<chain> [ file1 file2 file_n ]    (Old Syntax) \n"
-  "\n"
-  "        For example the call:\n"
-  "            -merge [ -select,name=topo : *.grb ]\n"
-  "        would merge all grib files in the folder after selecting the variable topo from them\n"
-  "\n"
-  "        The example:\n"
-  "            \"-merge [ -addc,1 -mulc,2 : -add file1 file2 -subc,1 file3 file4 ] out\""
-  "        would result in:\n"
-  "            -merge -addc,1 -mulc,2 -add file1 file2 -addc,1 -mulc,2 -subc,3 file3 -addc,2 -mulc,23 file4 out\n"
-  "\n"
-  "        In combination with the subgroup (see --argument_groups) feature this allows rather complex calls\n"
-  "            -merge [ [ -addc,1 : *1991.grb ] -merge [ -mulc,23 : *1990.grb ] -add file3 file4 ] outfile \n";
-
-
-static std::string subgroup_help=
-  "        This feature allow to use multiple operators with variable number of inputs\n"
-  "        Notes:\n"
-  "            When a bracket is closed it is no longer possible to add aditional inputs.\n"
-  "            When a bracket is closed another variable input operator can be used without brackets\n"
-  "\n"
-  "        Where it is normally not possible to chain multiple operators of that kind, with subgroups a arbitrary number can be chained\n"
-  "            -merge -merge file1 operator file1 > error:\n"
-  "                it cannot be decided which inputs belong to which operator\n"
-  "            -merge [ -merge file1 operator ] file1 > success:\n"
-  "\n"
-  "        With the brackets it is possible to have multiple variable inputs as inputs for another variable input\n"
-  "            -merge [ -merge [ *.grb ] -merge [ *.nc ] ] out\n"
-  "        In combination with the apply (see --apply) feature this allows rather complex calls\n"
-  "            -merge [ [ -addc,1 : *1991.grb ] -merge [ -mulc,23 : *1990.grb ] -add file3 file4 ] outfile \n";
-
-
+static std::string apply_help
+    = "        This feature allows to prepend simple cdo chains to other chains\n"
+      "        Apply syntax:\n"
+      "            (1) [ chain : file1 file2 file ]             (Recommended Syntax)\n"
+      "            (2) -apply,<chain> [ file1 file2 file_n ]    (Old Syntax) \n"
+      "\n"
+      "        For example the call:\n"
+      "            -merge [ -select,name=topo : *.grb ]\n"
+      "        would merge all grib files in the folder after selecting the variable topo from them\n"
+      "\n"
+      "        The example:\n"
+      "            \"-merge [ -addc,1 -mulc,2 : -add file1 file2 -subc,1 file3 file4 ] out\""
+      "        would result in:\n"
+      "            -merge -addc,1 -mulc,2 -add file1 file2 -addc,1 -mulc,2 -subc,3 file3 -addc,2 -mulc,23 file4 out\n"
+      "\n"
+      "        In combination with the subgroup (see --argument_groups) feature this allows rather complex calls\n"
+      "            -merge [ [ -addc,1 : *1991.grb ] -merge [ -mulc,23 : *1990.grb ] -add file3 file4 ] outfile \n";
+
+static std::string subgroup_help
+    = "        This feature allow to use multiple operators with variable number of inputs\n"
+      "        Notes:\n"
+      "            When a bracket is closed it is no longer possible to add aditional inputs.\n"
+      "            When a bracket is closed another variable input operator can be used without brackets\n"
+      "\n"
+      "        Where it is normally not possible to chain multiple operators of that kind, with subgroups a arbitrary number can "
+      "be chained\n"
+      "            -merge -merge file1 operator file1 > error:\n"
+      "                it cannot be decided which inputs belong to which operator\n"
+      "            -merge [ -merge file1 operator ] file1 > success:\n"
+      "\n"
+      "        With the brackets it is possible to have multiple variable inputs as inputs for another variable input\n"
+      "            -merge [ -merge [ *.grb ] -merge [ *.nc ] ] out\n"
+      "        In combination with the apply (see --apply) feature this allows rather complex calls\n"
+      "            -merge [ [ -addc,1 : *1991.grb ] -merge [ -mulc,23 : *1990.grb ] -add file3 file4 ] outfile \n";
 
 //'Regular' parser messages
-static std::string errmsg_multiple_variable = "Operator cannot be assigned.\n       Reason:\n         Multiple variable input operators used.\n         Use subgroups via [ ] to clarify relations (help: --argument_groups).\n";
+static std::string errmsg_multiple_variable
+    = "Operator cannot be assigned.\n       Reason:\n         Multiple variable input operators used.\n         Use subgroups via "
+      "[ ] to clarify relations (help: --argument_groups).\n";
 static std::string errmsg_missing_outputs = "Missing outputs";
 static std::string errmsg_missing_inputs = "Missing inputs";
-static std::string errmsg_unprocessed_inputs = "Operator cannot be assigned.\n       Reason:\n         No Operators with missing input left.\n";
+static std::string errmsg_unprocessed_inputs
+    = "Operator cannot be assigned.\n       Reason:\n         No Operators with missing input left.\n";
 static std::string errmsg_keyword_output = "Keywords cannot be used as file names";
+static std::string errmsg_not_in_first_position = "This operator can't be combined with other operators!";
 
-//Subgroup errors
+// Subgroup errors
 static std::string errmsg_mixed_input = "Mixing of normal inputs and subgroups is not allowed";
 static std::string errmsg_missing_sub_group = "Closing bracket without open subgroup";
 static std::string errmsg_empty_subgroup = "Empty Subgroup";
 static std::string errmsg_bracket_not_closed = "Bracket not closed";
 static std::string errmsg_malformed_subgroup = "Malformed Subgroup";
 
-//Apply error messages
+// Apply error messages
 static std::string errmsg_only_1_to_1_operators = "Only operators with a single in and output allowed";
 static std::string errmsg_apply_missing_argument = "Missing arguments";
 static std::string errmsg_apply_multiple_roots = "Apply can only process chains with a single in and out put";
@@ -76,6 +78,7 @@ std::vector<std::shared_ptr<Node>> _parse(std::vector<std::string> p_argv);
 
 namespace Util
 {
+void extract_name_and_argument(const std::string &command, std::string &operatorName, std::string &operatorArgument);
 
 std::string result_to_string(std::vector<std::shared_ptr<Node>> p_roots, std::string p_text = "returning: ");
 
diff --git a/src/percentiles_hist.cc b/src/percentiles_hist.cc
index 939e57f4b4b10e30f863ca10ab33e44a09204f85..37f2ec6b751ac1d83e0e9bb57e7cb0ed8961b803 100644
--- a/src/percentiles_hist.cc
+++ b/src/percentiles_hist.cc
@@ -40,7 +40,7 @@ hist_init_bins(int nbins, T *bins)
 }
 
 static void
-histDefBounds(Histogram &hist, float a, float b)
+histDefBounds(HistogramEntry &hist, float a, float b)
 {
   assert(hist.nbins > 0);
 
@@ -63,7 +63,7 @@ calc_bin(int nbins, float histMin, float histStep, float value)
 
 template <typename T>
 static void
-histBinAddValue(Histogram &hist, T *bins, float value)
+histBinAddValue(HistogramEntry &hist, T *bins, float value)
 {
   auto bin = calc_bin(hist.nbins, hist.min, hist.step, value);
   if (bin >= 0 && bin < hist.nbins) bins[bin]++;
@@ -71,14 +71,14 @@ histBinAddValue(Histogram &hist, T *bins, float value)
 
 template <typename T>
 static void
-histBinSubValue(Histogram &hist, T *bins, float value)
+histBinSubValue(HistogramEntry &hist, T *bins, float value)
 {
   auto bin = calc_bin(hist.nbins, hist.min, hist.step, value);
   if (bin >= 0 && bin < hist.nbins && bins[bin] > 0) bins[bin]--;
 }
 
 static void
-histBin(Histogram &hist)
+histBin(HistogramEntry &hist)
 {
   assert(hist.nsamp == hist.capacity);
 
@@ -99,7 +99,7 @@ histBin(Histogram &hist)
 }
 /* unused
 static int
-histReset(Histogram &hist)
+histReset(HistogramEntry &hist)
 {
   assert(hist.nbins > 0);
 
@@ -122,7 +122,7 @@ histReset(Histogram &hist)
 */
 
 static void
-histCheckValue(const Histogram &hist, float &value)
+histCheckValue(const HistogramEntry &hist, float &value)
 {
   // 2011-08-01 Uwe Schulzweida: added check for rounding errors
   if (value < hist.min && (hist.min - value) < 1.e5f) value = hist.min;
@@ -130,7 +130,7 @@ histCheckValue(const Histogram &hist, float &value)
 }
 
 static int
-histAddValue(Histogram &hist, float value)
+histAddValue(HistogramEntry &hist, float value)
 {
   assert(hist.nbins > 0);
 
@@ -156,7 +156,7 @@ histAddValue(Histogram &hist, float value)
 }
 
 static void
-histRemoveValue(Histogram &hist, float value)
+histRemoveValue(HistogramEntry &hist, float value)
 {
   auto fltptr = FLT_PTR(hist.ptr);
 
@@ -176,7 +176,7 @@ histRemoveValue(Histogram &hist, float value)
 }
 
 static int
-histSubValue(Histogram &hist, float value)
+histSubValue(HistogramEntry &hist, float value)
 {
   assert(hist.nbins > 0);
 
@@ -220,7 +220,7 @@ histGetBin(int nbins, double s, const T *ptr)
 }
 
 static double
-histGetPercentile(const Histogram &hist, double p)
+histGetPercentile(const HistogramEntry &hist, double p)
 {
   assert(hist.nsamp > 0);
   assert(hist.nbins > 0);
@@ -240,7 +240,7 @@ histGetPercentile(const Histogram &hist, double p)
 
       auto bin = hist.isUint32 ? histGetBin(hist.nbins, s, INT_PTR(hist.ptr)) : histGetBin(hist.nbins, s, SHR_PTR(hist.ptr));
 
-      //assert(hist.step > 0.0f);
+      // assert(hist.step > 0.0f);
 
       return hist.min + bin * hist.step;
     }
@@ -286,7 +286,7 @@ HistogramSet::createVarLevels(int varID, int nlevels, size_t nhists)
 
 template <typename T1, typename T2>
 static void
-def_bounds(size_t nhists, std::vector<Histogram> &hists, const Varray<T1> &v1, const Varray<T2> &v2, float mv1, float mv2)
+def_bounds(size_t nhists, std::vector<HistogramEntry> &hists, const Varray<T1> &v1, const Varray<T2> &v2, float mv1, float mv2)
 {
   assert(!v1.empty());
   assert(!v2.empty());
@@ -333,12 +333,12 @@ HistogramSet::defVarLevelBounds(int varID, int levelID, const Field &field1, con
 
 template <typename T>
 static int
-histAddVarLevelValues(size_t nhists, std::vector<Histogram> &hists, const Varray<T> &v, size_t nmiss, T mv)
+histAddVarLevelValues(size_t nhists, std::vector<HistogramEntry> &hists, const Varray<T> &v, size_t numMissVals, T mv)
 {
   assert(!v.empty());
 
   int nign = 0;
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < nhists; ++i)
         if (!dbl_is_equal(v[i], mv)) nign += histAddValue(hists[i], v[i]);
@@ -353,12 +353,12 @@ histAddVarLevelValues(size_t nhists, std::vector<Histogram> &hists, const Varray
 
 template <typename T>
 static int
-histSubVarLevelValues(size_t nhists, std::vector<Histogram> &hists, const Varray<T> &v, size_t nmiss, T mv)
+histSubVarLevelValues(size_t nhists, std::vector<HistogramEntry> &hists, const Varray<T> &v, size_t numMissVals, T mv)
 {
   assert(!v.empty());
 
   int nign = 0;
-  if (nmiss)
+  if (numMissVals)
     {
       for (size_t i = 0; i < nhists; ++i)
         if (!dbl_is_equal(v[i], mv)) nign += histSubValue(hists[i], v[i]);
@@ -387,9 +387,9 @@ HistogramSet::addVarLevelValues(int varID, int levelID, const Field &field)
   int nign = 0;
 
   if (field.memType == MemType::Float)
-    nign = histAddVarLevelValues(nhists, hists, field.vec_f, field.nmiss, (float) field.missval);
+    nign = histAddVarLevelValues(nhists, hists, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    nign = histAddVarLevelValues(nhists, hists, field.vec_d, field.nmiss, field.missval);
+    nign = histAddVarLevelValues(nhists, hists, field.vec_d, field.numMissVals, field.missval);
 
   if (nign)
     {
@@ -416,9 +416,9 @@ HistogramSet::subVarLevelValues(int varID, int levelID, const Field &field)
   int nign = 0;
 
   if (field.memType == MemType::Float)
-    nign = histSubVarLevelValues(nhists, hists, field.vec_f, field.nmiss, (float) field.missval);
+    nign = histSubVarLevelValues(nhists, hists, field.vec_f, field.numMissVals, (float) field.missval);
   else
-    nign = histSubVarLevelValues(nhists, hists, field.vec_d, field.nmiss, field.missval);
+    nign = histSubVarLevelValues(nhists, hists, field.vec_d, field.numMissVals, field.missval);
 
   if (nign)
     {
@@ -451,26 +451,23 @@ HistogramSet::reset(int varID, int levelID)
 
 template <typename T>
 static size_t
-calcPercentile(size_t nhists, const std::vector<Histogram> &hists, double p, Varray<T> &v, T mv)
+calcPercentile(size_t nhists, const std::vector<HistogramEntry> &hists, double p, Varray<T> &v, T mv)
 {
   assert(!v.empty());
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
 
   for (size_t i = 0; i < nhists; ++i)
     {
-      if (hists[i].nsamp)
-        {
-          v[i] = histGetPercentile(hists[i], p);
-        }
+      if (hists[i].nsamp) { v[i] = histGetPercentile(hists[i], p); }
       else
         {
           v[i] = mv;
-          nmiss++;
+          numMissVals++;
         }
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 void
@@ -487,7 +484,7 @@ HistogramSet::getVarLevelPercentiles(Field &field, int varID, int levelID, doubl
   const auto &hists = this->histograms[varID][levelID];
 
   if (field.memType == MemType::Float)
-    field.nmiss = calcPercentile(nhists, hists, p, field.vec_f, (float) field.missval);
+    field.numMissVals = calcPercentile(nhists, hists, p, field.vec_f, (float) field.missval);
   else
-    field.nmiss = calcPercentile(nhists, hists, p, field.vec_d, field.missval);
+    field.numMissVals = calcPercentile(nhists, hists, p, field.vec_d, field.missval);
 }
diff --git a/src/percentiles_hist.h b/src/percentiles_hist.h
index e17eed84380f90e56f6996f4956730c83335a822..6c1027dce7235add59e97a3735e902d4909361a7 100644
--- a/src/percentiles_hist.h
+++ b/src/percentiles_hist.h
@@ -5,7 +5,7 @@
 
 #include "field.h"
 
-struct Histogram
+struct HistogramEntry
 {
   void *ptr = nullptr;
   float min = 0.0f;
@@ -30,7 +30,7 @@ private:
   int nsteps = 0;
   std::vector<int> var_nlevels;
   std::vector<size_t> var_nhists;
-  std::vector<std::vector<std::vector<Histogram>>> histograms;
+  std::vector<std::vector<std::vector<HistogramEntry>>> histograms;
 
   void
   init()
diff --git a/src/pipe.cc b/src/pipe.cc
index 67acd23a70111d18fbc8d8bbeb164bbab488db5c..ad78aa35fd950c099a75c233de0466e7439eb491 100644
--- a/src/pipe.cc
+++ b/src/pipe.cc
@@ -43,7 +43,7 @@ pipe_t::pipe_init()
   levelID = -1;
 
   nrecs = 0;
-  nmiss = 0;
+  numMissVals = 0;
   data_d = nullptr;
   data_f = nullptr;
   hasdata = false;
@@ -71,7 +71,7 @@ pipe_t::pipe_inq_timestep(int p_tsID)
       {
         if (EOP)
           {
-            Debug(PIPE,  "%s EOP",name.c_str());
+            Debug(PIPE, "%s EOP", name.c_str());
             break;
           }
         if (hasdata)
@@ -83,7 +83,7 @@ pipe_t::pipe_inq_timestep(int p_tsID)
             data_is_float = false;
             read_cond.notify_all();
           }
-          Debug(PIPE && !hasdata, "%s has no data", name);
+        Debug(PIPE && !hasdata, "%s has no data", name);
 
         recInq_cond.notify_all(); /* o.k. ??? */
 
@@ -125,7 +125,7 @@ pipe_t::pipe_inq_vlist(int &p_vlistID)
   while (p_vlistID == -1 && nwaitcycles < maxWaitCycles && !EOP)
     {
       time_to_wait += timeOut;
-      Debug(PIPE,"%s wait of vlistDef_cond", name.c_str());
+      Debug(PIPE, "%s wait of vlistDef_cond", name.c_str());
       vlistDef_cond.wait_for(lock, time_to_wait);
       nwaitcycles++;
     }
@@ -182,7 +182,7 @@ pipe_t::pipe_inq_record(int *p_varID, int *p_levelID)
 
   {
     std::scoped_lock lock(m_mutex);
-    Debug(PIPE,"%s has no data %d %d ", name.c_str(),  recIDr, " ", recIDw);
+    Debug(PIPE, "%s has no data %d %d ", name.c_str(), recIDr, " ", recIDw);
     if (hasdata || usedata)
       {
         hasdata = false;
@@ -277,7 +277,7 @@ pipe_t::pipe_def_record(int p_varID, int p_levelID)
  * @param pipe pipe that has the wanted data
  */
 size_t
-pipe_t::pipe_read_pipe_record(double *const p_data, int vlistID, size_t *const p_nmiss)
+pipe_t::pipe_read_pipe_record(double *const p_data, int vlistID, size_t *const p_numMissVals)
 {
   if (!p_data) cdo_abort("No data pointer for %s", name);
 
@@ -290,12 +290,12 @@ pipe_t::pipe_read_pipe_record(double *const p_data, int vlistID, size_t *const p
     }
   else { memcpy(p_data, data_d, datasize * sizeof(double)); }
 
-  *p_nmiss = nmiss;
+  *p_numMissVals = numMissVals;
   return datasize;
 }
 
 size_t
-pipe_t::pipe_read_pipe_record(float *const p_data, int vlistID, size_t *const p_nmiss)
+pipe_t::pipe_read_pipe_record(float *const p_data, int vlistID, size_t *const p_numMissVals)
 {
   if (!p_data) cdo_abort("No data pointer for %s", name);
 
@@ -308,28 +308,28 @@ pipe_t::pipe_read_pipe_record(float *const p_data, int vlistID, size_t *const p_
       for (size_t i = 0; i < datasize; ++i) p_data[i] = (float) data_d[i];
     }
 
-  *p_nmiss = nmiss;
+  *p_numMissVals = numMissVals;
   return datasize;
 }
 
 size_t
-pipe_t::pipe_read_record(int p_vlistID, double *const p_data, size_t *const p_nmiss)
+pipe_t::pipe_read_record(int p_vlistID, double *const p_data, size_t *const p_numMissVals)
 {
-  *p_nmiss = 0;
+  *p_numMissVals = 0;
   size_t nvals = 0;
 
   {
     std::unique_lock<std::mutex> lock(m_mutex);
     while (!hasdata)
       {
-        Debug(PIPE,"%s wait of write_cond", name.c_str());
+        Debug(PIPE, "%s wait of write_cond", name.c_str());
         write_cond.wait(lock);
       }
 
-    if (hasdata) { nvals = pipe_read_pipe_record(p_data, p_vlistID, p_nmiss); }
+    if (hasdata) { nvals = pipe_read_pipe_record(p_data, p_vlistID, p_numMissVals); }
     else { cdo_abort("data type %d not implemented", hasdata); }
 
-    Debug(PIPE,  "%s read record %d",name.c_str(), recIDr);
+    Debug(PIPE, "%s read record %d", name.c_str(), recIDr);
 
     hasdata = false;
     data_d = nullptr;
@@ -341,9 +341,9 @@ pipe_t::pipe_read_record(int p_vlistID, double *const p_data, size_t *const p_nm
 }
 
 size_t
-pipe_t::pipe_read_record(int p_vlistID, float *const p_data, size_t *const p_nmiss)
+pipe_t::pipe_read_record(int p_vlistID, float *const p_data, size_t *const p_numMissVals)
 {
-  *p_nmiss = 0;
+  *p_numMissVals = 0;
   size_t nvals = 0;
 
   {
@@ -354,7 +354,7 @@ pipe_t::pipe_read_record(int p_vlistID, float *const p_data, size_t *const p_nmi
         write_cond.wait(lock);
       }
 
-    if (hasdata) { nvals = pipe_read_pipe_record(p_data, p_vlistID, p_nmiss); }
+    if (hasdata) { nvals = pipe_read_pipe_record(p_data, p_vlistID, p_numMissVals); }
     else { cdo_abort("data type %d not implemented", hasdata); }
 
     Debug(PIPE, "%s read record %d", name.c_str(), recIDr);
@@ -369,9 +369,9 @@ pipe_t::pipe_read_record(int p_vlistID, float *const p_data, size_t *const p_nmi
 }
 
 size_t
-pipe_t::pipe_read_record(int p_vlistID, Field *const p_field, size_t *const p_nmiss)
+pipe_t::pipe_read_record(int p_vlistID, Field *const p_field, size_t *const p_numMissVals)
 {
-  return pipe_read_record(p_vlistID, p_field->vec_d.data(), p_nmiss);
+  return pipe_read_record(p_vlistID, p_field->vec_d.data(), p_numMissVals);
 }
 
 void
@@ -398,37 +398,37 @@ pipe_t::wait_for_read()
 }
 
 void
-pipe_t::pipe_write_record(const double *const p_data, size_t p_nmiss)
+pipe_t::pipe_write_record(const double *const p_data, size_t p_numMissVals)
 {
   {
     std::scoped_lock lock(m_mutex);
     hasdata = true;  // data pointer
     data_is_float = false;
     data_d = (double *) p_data;
-    nmiss = p_nmiss;
+    numMissVals = p_numMissVals;
   }
 
   wait_for_read();
 }
 
 void
-pipe_t::pipe_write_record(const float *const p_data, size_t p_nmiss)
+pipe_t::pipe_write_record(const float *const p_data, size_t p_numMissVals)
 {
   {
     std::scoped_lock lock(m_mutex);
     hasdata = true;  // data pointer
     data_is_float = true;
     data_f = (float *) p_data;
-    nmiss = p_nmiss;
+    numMissVals = p_numMissVals;
   }
 
   wait_for_read();
 }
 
 void
-pipe_t::pipe_write_record(const Field *const p_field, size_t p_nmiss)
+pipe_t::pipe_write_record(const Field *const p_field, size_t p_numMissVals)
 {
-  pipe_write_record(p_field->vec_d.data(), p_nmiss);
+  pipe_write_record(p_field->vec_d.data(), p_numMissVals);
 }
 
 void
diff --git a/src/pipe.h b/src/pipe.h
index a9961760c8ca1a36aa43c50802d7ee17b9019a3d..d8b8a428e68f97f718a03c44ae874c5d47bcaa30 100644
--- a/src/pipe.h
+++ b/src/pipe.h
@@ -27,16 +27,16 @@ public:
   int pipe_inq_record(int *varID, int *levelID);
   void pipe_def_record(int p_varId, int p_levelID);
 
-  void pipe_write_record(const double *const p_data, size_t p_nmiss);
-  void pipe_write_record(const float *const p_data, size_t p_nmiss);
-  void pipe_write_record(const Field *const p_data, size_t p_nmiss);
+  void pipe_write_record(const double *const p_data, size_t p_numMissVals);
+  void pipe_write_record(const float *const p_data, size_t p_numMissVals);
+  void pipe_write_record(const Field *const p_data, size_t p_numMissVals);
 
-  size_t pipe_read_record(int p_vlistID, double *data, size_t *nmiss);
-  size_t pipe_read_record(int p_vlistID, float *data, size_t *nmiss);
-  size_t pipe_read_record(int p_vlistID, Field *data, size_t *nmiss);
+  size_t pipe_read_record(int p_vlistID, double *data, size_t *numMissVals);
+  size_t pipe_read_record(int p_vlistID, float *data, size_t *numMissVals);
+  size_t pipe_read_record(int p_vlistID, Field *data, size_t *numMissVals);
 
-  size_t pipe_read_pipe_record(double *data, int vlistID, size_t *p_nmiss);
-  size_t pipe_read_pipe_record(float *data, int vlistID, size_t *p_nmiss);
+  size_t pipe_read_pipe_record(double *data, int vlistID, size_t *p_numMissVals);
+  size_t pipe_read_pipe_record(float *data, int vlistID, size_t *p_numMissVals);
 
   void pipe_set_name(int processID, int inputIDX);
   void close();
@@ -48,7 +48,7 @@ public:
   int varID, levelID;
   int recIDr, recIDw, tsIDr, tsIDw;
 
-  size_t nmiss;
+  size_t numMissVals;
   int nrecs;
 
   bool data_is_float;
diff --git a/src/pipeStream.cc b/src/pipeStream.cc
index 320413eb7bdad15d34ea741b0444c88bc10c8fec..612c6dd13dd61729dc49a0f1967b958fdb447964 100644
--- a/src/pipeStream.cc
+++ b/src/pipeStream.cc
@@ -83,39 +83,39 @@ PipeStream::defRecord(int varID, int levelID)
 }
 
 void
-PipeStream::read_record(float *p_data, size_t *p_nmiss)
+PipeStream::read_record(float *p_data, size_t *p_numMissVals)
 {
-  m_nvals += m_pipe->pipe_read_record(m_vlistID, p_data, p_nmiss);
+  m_nvals += m_pipe->pipe_read_record(m_vlistID, p_data, p_numMissVals);
 }
 
 void
-PipeStream::read_record(double *p_data, size_t *p_nmiss)
+PipeStream::read_record(double *p_data, size_t *p_numMissVals)
 {
-  m_nvals += m_pipe->pipe_read_record(m_vlistID, p_data, p_nmiss);
+  m_nvals += m_pipe->pipe_read_record(m_vlistID, p_data, p_numMissVals);
 }
 
 void
-PipeStream::read_record(Field *p_field, size_t *p_nmiss)
+PipeStream::read_record(Field *p_field, size_t *p_numMissVals)
 {
-  m_nvals += m_pipe->pipe_read_record(m_vlistID, p_field, p_nmiss);
+  m_nvals += m_pipe->pipe_read_record(m_vlistID, p_field, p_numMissVals);
 }
 
 void
-PipeStream::write_record(const float *p_data, size_t p_nmiss)
+PipeStream::write_record(const float *p_data, size_t p_numMissVals)
 {
-  m_pipe->pipe_write_record(p_data, p_nmiss);
+  m_pipe->pipe_write_record(p_data, p_numMissVals);
 }
 
 void
-PipeStream::write_record(const double *p_data, size_t p_nmiss)
+PipeStream::write_record(const double *p_data, size_t p_numMissVals)
 {
-  m_pipe->pipe_write_record(p_data, p_nmiss);
+  m_pipe->pipe_write_record(p_data, p_numMissVals);
 }
 
 void
-PipeStream::write_record(const Field *p_field, size_t p_nmiss)
+PipeStream::write_record(const Field *p_field, size_t p_numMissVals)
 {
-  m_pipe->pipe_write_record(p_field, p_nmiss);
+  m_pipe->pipe_write_record(p_field, p_numMissVals);
 }
 
 void
diff --git a/src/pipeStream.h b/src/pipeStream.h
index 2f9fa85925e4385855afae01ace42dc4eba7d563..dc4d72ad9903b07179a7f4232bd98a69c510727e 100644
--- a/src/pipeStream.h
+++ b/src/pipeStream.h
@@ -33,13 +33,13 @@ public:
   void inq_record(int *varID, int *levelID) override;
   void defRecord(int varID, int levelID) override;
 
-  void read_record(float *const p_data, size_t *nmiss) override;
-  void read_record(double *const p_data, size_t *nmiss) override;
-  void read_record(Field *const p_field, size_t *nmiss) override;
+  void read_record(float *const p_data, size_t *numMissVals) override;
+  void read_record(double *const p_data, size_t *numMissVals) override;
+  void read_record(Field *const p_field, size_t *numMissVals) override;
 
-  void write_record(const float *const p_data, size_t nmiss) override;
-  void write_record(const double *const p_data, size_t nmiss) override;
-  void write_record(const Field *const p_field, size_t nmiss) override;
+  void write_record(const float *const p_data, size_t numMissVals) override;
+  void write_record(const double *const p_data, size_t numMissVals) override;
+  void write_record(const Field *const p_field, size_t numMissVals) override;
 
   void copyRecord(CdoStreamID p_fileStream) override;
 
diff --git a/src/pmlist.cc b/src/pmlist.cc
index 5b7bd2d00b034327cda35bfdbbb45055f364e336..b31dc5e1aba687506ac64580068db709e4928e16 100644
--- a/src/pmlist.cc
+++ b/src/pmlist.cc
@@ -13,6 +13,7 @@
 #include "pmlist.h"
 #include "namelist.h"
 #include "cdo_output.h"
+#include "util_string.h"
 
 static void
 keyValuesPrint(FILE *fp, const KeyValues &keyValues)
@@ -29,42 +30,49 @@ KVList::print(FILE *fp) const
 }
 
 int
-KVList::parse_arguments(int argc, const std::vector<std::string> &argv)
+KVList::parse_arguments(const std::vector<std::string> &argv)
 {
-  // Assume key = value pairs. That is, if argv[i] contains no '=' then treat it as if it belongs to the values of argv[i-1].
-  char key[256];
-  int i = 0;
-  while (i < argc)
+  /*  this function assumes input in the form of
+   *  value_name1=10,2,3,4,value_name2=201,23
+   **/
+
+  size_t equalPos = argv[0].find('=');
+  if (equalPos == std::string::npos)
+    {
+      fprintf(stderr, "missing '=' in key/value string: >%s<\n", argv[0].c_str());
+      return -1;
+    }
+
+  for (auto &it : argv)
     {
-      const char *currentArgv = argv[i].c_str();
-      const char *end = strchr(currentArgv, '=');
-      if (end == nullptr)
+      equalPos = it.find('=');
+      if (equalPos != std::string::npos)
         {
-          fprintf(stderr, "Missing '=' in key/value string: >%s<\n", currentArgv);
-          return -1;
+          auto key = it.substr(0, equalPos);
+
+          std::string value = Util::String::trim(it.substr(equalPos + 1));
+          {  // value vector gets moved at the end of scope
+            std::vector<std::string> value_vector;
+
+            /* clang-format off */
+            if (value.empty()) { value_vector = {};        }
+            else               { value_vector = { value }; }
+            /* clang-format on */
+
+            push_back(KeyValues());
+            KeyValues &newValue = back();
+            newValue.nvalues = 0;
+            newValue.values = std::move(value_vector);
+            newValue.key = key;
+          }
+          back().nvalues = back().values.size();
+        }
+      else
+        {
+          auto &last = back();
+          last.values.push_back(it);
+          last.nvalues = last.values.size();
         }
-
-      std::snprintf(key, sizeof(key), "%.*s", (int) (end - currentArgv), currentArgv);
-      key[sizeof(key) - 1] = 0;
-
-      int j = 1;
-      while (i + j < argc && strchr(argv[i + j].c_str(), '=') == nullptr) j++;
-
-      int nvalues = j;
-
-      KeyValues kv;
-      kv.values.resize(1);
-      kv.values[0] = end + 1;
-      if (kv.values[0][0] == 0) nvalues = 0;
-
-      kv.key = key;
-      kv.nvalues = nvalues;
-      kv.values.resize(nvalues);
-
-      for (j = 1; j < nvalues; ++j) kv.values[j] = argv[i + j];
-      this->push_back(kv);
-
-      i += j;
     }
 
   return 0;
@@ -100,6 +108,22 @@ KVList::append(const char *key, const char *const *values, int nvalues)
   this->push_back(kv);
 }
 
+void
+KVList::append(std::string &key, std::vector<std::string> values)
+{
+  KeyValues kv;
+  kv.key = key;
+  kv.values = values;
+  kv.nvalues = kv.values.size();
+  this->push_back(kv);
+}
+
+void
+KVList::append(std::string &key, std::string &values)
+{
+  append(key, std::vector<std::string>{ values });
+}
+
 // Remove only one list item
 void
 KVList::remove(const std::string &inkey)
diff --git a/src/pmlist.h b/src/pmlist.h
index 5b4ee1b6ae64f837bb1bcef0bcf50deca78aa0ab..d834b1c8e098fe5723e03484a5528cf520bcdf5e 100644
--- a/src/pmlist.h
+++ b/src/pmlist.h
@@ -33,10 +33,12 @@ public:
   std::string name;
 
   void print(FILE *fp = stderr) const;
-  int parse_arguments(int argc, const std::vector<std::string> &argv);
+  int parse_arguments(const std::vector<std::string> &argv);
   const KeyValues *search(const std::string &key) const;
   void remove(const std::string &inkey);
   void append(const char *, const char *const *, int);
+  void append(std::string &key, std::vector<std::string> values);
+  void append(std::string &key, std::string &value);
   char *get_first_value(const char *key, const char *replacer);
 };
 
diff --git a/src/printinfo.cc b/src/printinfo.cc
index be41e990a5c890011e56bf81f851231fd1991066..71267c7b3a4f37b9f80588a3810e74593150935f 100644
--- a/src/printinfo.cc
+++ b/src/printinfo.cc
@@ -635,7 +635,7 @@ print_zaxis_info(int vlistID)
       if (zaxisInqLbounds(zaxisID, NULL) && zaxisInqUbounds(zaxisID, NULL))
         printZaxisBoundsInfo(zaxisID, dig, levelsize, zinc, zunits);
 
-      if (zaxistype == ZAXIS_HYBRID) printZaxisHybridInfo(zaxisID);
+      if (zaxistype == ZAXIS_HYBRID || zaxistype == ZAXIS_HYBRID_HALF) printZaxisHybridInfo(zaxisID);
 
       if (zaxistype == ZAXIS_REFERENCE) printZaxisReferenceInfo(zaxisID);
 
diff --git a/src/process.cc b/src/process.cc
index fc1c4e679d747344b997a18b8e021ccb9d1e62aa..38eb6960c12db17168e27c0a4c6beece8a0cd47a 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -30,6 +30,9 @@
 #include "fileStream.h"
 #include "pipeStream.h"
 
+// temporary include for setting the local process
+#include "process_int.h"
+
 static int processNum = 0;
 
 int
@@ -44,32 +47,25 @@ set_process_num(int p_num)
   processNum = p_num;
 }
 
-Process::Process(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments)
-    : m_ID(p_ID), operatorName(p_operatorName)
-{
-  m_isActive = true;
-  init_process(p_operatorName, p_arguments);
-}
-
-bool
-Process::input_is_variable()
+std::string
+Process::replace_alias(const std::string &p_calledBy, const CdoModule &p_module)
 {
-  return (m_module.get_stream_in_cnt() == -1);
+  std::string originalName = p_calledBy;
+  int aliasID = p_module.is_alias(p_calledBy);
+  if (aliasID != -1) originalName = p_module.aliases[aliasID].original;
+  return originalName;
 }
 
-void
-Process::init_process(const std::string &p_operatorName, const std::vector<std::string> &p_arguments)
+Process::Process(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments,
+                 const CdoModule &p_module)
+    : m_ID(p_ID), m_module(p_module), m_oargv(p_arguments)
 {
-  startTime = cdo::get_wtime();
 #ifdef HAVE_LIBPTHREAD
   threadID = pthread_self();
 #endif
-  m_oargv = p_arguments;
-  operatorName = get_original(p_operatorName);
 
-  m_module = get_module(p_operatorName);
-
-  def_prompt();  // has to be called after get operatorName
+  operatorName = replace_alias(p_operatorName, p_module);
+  def_prompt(operatorName);
 }
 
 int
@@ -85,12 +81,12 @@ Process::get_stream_cnt_out()
 }
 
 void
-Process::def_prompt()
+Process::def_prompt(const std::string &name)
 {
   if (m_ID == 0)
-    std::snprintf(prompt, sizeof(prompt), "%s    %s", cdo::progname, operatorName.c_str());
+    std::snprintf(prompt, sizeof(prompt), "%s    %s", cdo::progname, name.c_str());
   else
-    std::snprintf(prompt, sizeof(prompt), "%s(%d) %s", cdo::progname, m_ID, operatorName.c_str());
+    std::snprintf(prompt, sizeof(prompt), "%s(%d) %s", cdo::progname, m_ID, name.c_str());
 }
 
 const char *
@@ -99,149 +95,19 @@ Process::inq_prompt() const
   return prompt;
 }
 
-void
-Process::handle_process_err()
-{
-  switch (m_status)
-    {
-    case ProcessStatus::UnlimitedIOCounts:
-      {
-        cdo_abort("I/O stream counts unlimited no allowed!");
-        break;
-      }
-    case ProcessStatus::MissInput:
-      {
-        cdo_abort("Input streams missing!");
-        break;
-      }
-    case ProcessStatus::MissOutput:
-      {
-        cdo_abort("Output streams missing!");
-        break;
-      }
-    case ProcessStatus::TooManyStreams:
-    case ProcessStatus::TooFewStreams:
-      {
-        auto inCnt = m_module.get_stream_in_cnt();
-        auto outCnt = m_module.get_stream_out_cnt();
-        bool lobase = (outCnt == -1);
-        if (lobase) outCnt = 1;
-
-        std::string caseCount = (m_status == ProcessStatus::TooManyStreams) ? "many" : "few";
-        std::string pluralIn = (inCnt > 1) ? "s" : "";
-        std::string pluralOut = (outCnt > 1) ? "s" : "";
-
-        std::stringstream errMsg;
-        errMsg << "Too " << caseCount << " streams specified! Operator " << operatorName << " needs " << inCnt << " input stream"
-               << pluralIn << " and " << outCnt << " output " << (lobase ? "basename" : "stream") << pluralOut << "!";
-        cdo_abort(errMsg.str());
-        break;
-      }
-    case ProcessStatus::Ok: break;
-    }
-}
-
-void
-Process::validate()
-{
-  check_stream_cnt();
-  if (m_status != ProcessStatus::Ok) handle_process_err();
-}
-
-void
-Process::check_stream_cnt()
-{
-  int streamCnt = 0;
-
-  int wantedStreamInCnt = m_module.get_stream_in_cnt();
-  int wantedStreamOutCnt = m_module.get_stream_out_cnt();
-
-  int streamInCnt0 = wantedStreamInCnt;
-
-  bool obase = false;
-  if (wantedStreamOutCnt == -1)
-    {
-      wantedStreamOutCnt = 1;
-      obase = true;
-    }
-
-  if (wantedStreamInCnt == -1 && wantedStreamOutCnt == -1) { m_status = ProcessStatus::UnlimitedIOCounts; }
-
-  // printf(" wantedStreamInCnt,wantedStreamOutCnt %d %d\n",
-  // wantedStreamInCnt,wantedStreamOutCnt);
-  else if (wantedStreamInCnt == -1)
-    {
-      wantedStreamInCnt = m_streamCnt - wantedStreamOutCnt;
-      if (wantedStreamInCnt < 1) m_status = ProcessStatus::MissInput;
-    }
-
-  else if (wantedStreamOutCnt == -1)
-    {
-      wantedStreamOutCnt = m_streamCnt - wantedStreamInCnt;
-      if (wantedStreamOutCnt < 1) m_status = ProcessStatus::MissOutput;
-    }
-  else
-    {
-      // printf(" wantedStreamInCnt,wantedStreamOutCnt %d %d\n",
-      // wantedStreamInCnt,wantedStreamOutCnt);
-
-      streamCnt = wantedStreamInCnt + wantedStreamOutCnt;
-      // printf(" streamCnt %d %d\n", m_streamCnt, streamCnt);
-
-      if (m_streamCnt > streamCnt)
-        m_status = ProcessStatus::TooManyStreams;
-      else if (m_streamCnt < streamCnt && !obase)
-        m_status = ProcessStatus::TooFewStreams;
-      else if (wantedStreamInCnt > (int) inputStreams.size())
-        m_status = ProcessStatus::TooFewStreams;
-
-      else if (wantedStreamInCnt == 1 && streamInCnt0 == -1)
-        m_status = ProcessStatus::Ok;
-    }
-}
-
-bool
-Process::has_hall_inputs()
-{
-  if (m_module.get_stream_in_cnt() == -1) return false;
-
-  return (m_module.get_stream_in_cnt() == static_cast<short>(inputStreams.size()));
-}
-
-void
-Process::set_inactive()
-{
-  m_isActive = false;
-}
-
-int
-Process::operator_add(const char *name, int f1, int f2, const char *enter)
-{
-  int operID = m_noper;
-
-  if (operID >= MAX_OPERATOR) cdo_abort("Maximum number of %d operators reached!", MAX_OPERATOR);
-
-  oper[m_noper] = { f1, f2, name, enter };
-
-  m_noper++;
-
-  return operID;
-}
-
 int
 Process::get_operator_id()
 {
-  if (m_noper > 0)
-    {
-      for (int operID = 0; operID < m_noper; operID++)
-        {
-          if (operatorName == oper[operID].name) return operID;
-        }
+  if (m_module.operators.size() <= 0) { cdo_abort("Operator not initialized!"); }
 
-      cdo_abort("Operator not callable by this name! Name is: %s", operatorName);
+  Debug(PROCESS, "searching for %s", operatorName);
+  for (size_t operID = 0; operID < m_module.operators.size(); operID++)
+    {
+      Debug(PROCESS, "comparing  %s and %s", operatorName, m_module.operators[operID].name);
+      if (operatorName == m_module.operators[operID].name) return operID;
     }
 
-  cdo_abort("Operator not initialized!");
+  cdo_abort("Operator not callable by this name! Name is: %s", operatorName);
 
   return -1;
 }
@@ -265,7 +131,6 @@ void
 Process::add_child(const std::shared_ptr<Process> &childProcess)
 {
   childProcesses.push_back(childProcess);
-  nchild = childProcesses.size();
   add_pipe_in_stream();
 }
 
@@ -295,8 +160,25 @@ Process::add_pipe_out_stream()
   m_streamCnt++;
 }
 
+void *
+execute(void *process)
+{
+
+  Process *p = (Process *) process;
+
+  p->cdo_initialize();
+
+  p->init();
+  p->run();
+  p->close();
+
+  p->cdo_finish();
+
+  return nullptr;
+}
+
 pthread_t
-Process::run()
+Process::start_thread()
 {
   Debug(PROCESS, "starting new thread for process %d", m_ID);
   pthread_attr_t attr;
@@ -329,80 +211,22 @@ Process::run()
     }
 
   pthread_t thrID;
-  auto rval = pthread_create(&thrID, &attr, m_module.func, this);
+  auto rval = pthread_create(&thrID, &attr, execute, this);
   if (rval != 0)
     {
       errno = rval;
       cdo_sys_error("pthread_create failed for '%s'", operatorName.c_str());
     }
 
-  m_isActive = true;
-
   return thrID;
 }
 
 // local helper function
-extern "C" size_t getPeakRSS();
-static void
-get_max_memstring(char *p_memstring, size_t memstringLen)
-{
-  auto memmax = getPeakRSS();
-  if (memmax)
-    {
-      size_t muindex = 0;
-      const char *mu[] = { "B", "KB", "MB", "GB", "TB", "PB" };
-      size_t nmu = sizeof(mu) / sizeof(char *);
-      while (memmax > 9999 && muindex < nmu - 1)
-        {
-          memmax /= 1024;
-          muindex++;
-        }
-      std::snprintf(p_memstring, memstringLen, " %zu%s", memmax, mu[muindex]);
-    }
-}
-
-void
-Process::print_benchmarks(double p_walltime, const char *p_memstring)
-{
-  auto numberOfUsedThreads = get_process_num();
-  if (Options::test)
-    fprintf(stdout, " [%.2fs%s %dthread%s]", p_walltime, p_memstring, numberOfUsedThreads, ADD_PLURAL(numberOfUsedThreads));
-  else
-    fprintf(stdout, " [%.2fs%s]", p_walltime, p_memstring);
-}
-
-void
-Process::print_processed_values()
-{
-  set_text_color(stdout, GREEN);
-  fprintf(stdout, "%s: ", prompt);
-  reset_text_color(stdout);
-
-  auto nvals = inq_nvals();
-
-  if (nvals > 0)
-    {
-      fprintf(stdout, "Processed %zu value%s from %d variable%s", nvals, ADD_PLURAL(nvals), m_nvars, ADD_PLURAL(m_nvars));
-    }
-  else if (m_nvars > 0) { fprintf(stdout, "Processed %d variable%s", m_nvars, ADD_PLURAL(m_nvars)); }
-
-  if ((nvals || m_nvars) && ntimesteps > 0) fprintf(stdout, " over %d timestep%s", ntimesteps, ADD_PLURAL(ntimesteps));
-
-  if (m_ID == 0)
-    {
-      char memstring[32] = { "" };
-      get_max_memstring(memstring, sizeof(memstring));
-      print_benchmarks(cdo::get_wtime() - startTime, memstring);
-    }
-
-  // if (m_nvars > 0 || nvals > 0 || ntimesteps > 0 || m_ID == 0) fprintf(stdout, ".");
-  fprintf(stdout, "\n");
-}
 
 bool
 Process::has_out_stream(CdoStreamID p_streamID)
 {
-  for (const CdoStreamID &streamID : outputStreams)
+  for (const auto &streamID : outputStreams)
     {
       if (streamID == p_streamID) return true;
     }
@@ -412,7 +236,7 @@ Process::has_out_stream(CdoStreamID p_streamID)
 bool
 Process::has_in_stream(CdoStreamID p_streamID)
 {
-  for (const CdoStreamID &streamID : inputStreams)
+  for (const auto &streamID : inputStreams)
     {
       if (streamID == p_streamID) return true;
     }
@@ -458,16 +282,23 @@ Process::get_argv(int p_idx)
   return m_oargv[p_idx];
 }
 
+void
+Process::set_obase(const std::string &obase)
+{
+  m_obase = obase;
+}  // TODO into cc
+
 const std::string
 Process::get_obase()
 {
   return m_obase;
 }
+
 void
 Process::close_streams()
 {
-  for (auto s : inputStreams) { s->close(); }
-  for (auto s : outputStreams) { s->close(); }
+  for (auto &s : inputStreams) { s->close(); }
+  for (auto &s : outputStreams) { s->close(); }
 }
 
 int
@@ -475,3 +306,28 @@ Process::get_id()
 {
   return m_ID;
 }
+void
+Process::cdo_initialize()
+{
+#if defined(_OPENMP)
+  omp_set_num_threads(Threading::ompNumThreads);  // Has to be called for every module (pthread)!
+#endif
+#ifdef HAVE_LIBPTHREAD
+  threadID = pthread_self();
+#endif
+  Debug(PROCESS_INT, "Initializing process: %s (id: %d)", operatorName, get_id());
+
+  set_local_process(this);
+#ifdef HAVE_LIBPTHREAD
+  Debug(PROCESS_INT, "process %d thread %ld", m_ID, pthread_self());
+#endif
+}
+void
+Process::cdo_finish(void)
+{
+  Debug(PROCESS_INT, "Finishing process: %d", get_id());
+
+#ifdef HAVE_LIBPTHREAD
+  Debug(PROCESS_INT, "process %d thread %ld", m_ID, pthread_self());
+#endif
+}
diff --git a/src/process.h b/src/process.h
index f5dc61254e8a85d75dc9df4bf1154f28d4b0d168..cd6d8fb24af71cad83d9b7b462edb1799cbfcb44 100644
--- a/src/process.h
+++ b/src/process.h
@@ -9,7 +9,8 @@
 #define PROCESS_H
 
 #include "cdoStream.h"
-#include "modules.h"
+#include "cdo_module.h"
+#include "cdo_timer.h"
 
 #include <vector>
 #include <iostream>
@@ -28,59 +29,46 @@ enum class ProcessStatus
   TooManyStreams = -4,
   TooFewStreams = -5,
 };
-
-class oper_t
-{
-public:
-  int f1 = 0;
-  int f2 = 0;
-  std::string name;
-  const char *enter = nullptr;
-
-  oper_t() {}
-  oper_t(int _f1, int _f2, const char *_name, const char *_enter) : f1(_f1), f2(_f2), name(_name), enter(_enter) {}
-  oper_t(const char *_name, int _f1, int _f2, const char *_enter) : f1(_f1), f2(_f2), name(_name), enter(_enter) {}
-
-  oper_t(const char *_name) : f1(0), f2(0), name(_name), enter(nullptr) {}
-};
+void *execute(void *process);
 
 class Process
 {
+
 public:
+  /* Member Variables */
+
   int m_ID;
+  const CdoModule &m_module;
   int m_posInParent;
-#ifdef HAVE_LIBPTHREAD
-  pthread_t threadID;
-#endif
-  short nchild = 0;
   std::vector<std::shared_ptr<Process>> childProcesses;
   std::vector<std::shared_ptr<Process>> parentProcesses;
   std::vector<CdoStreamID> inputStreams;
   std::vector<CdoStreamID> outputStreams;
-  int nChildActive = 0;
-
-  double startTime;
 
   int m_nvars = 0;
-
   int ntimesteps = 0;
   int m_streamCnt = 0;
+
+  char prompt[64];
+
   std::string m_operatorCommand = "UNINITALIZED";
   std::string operatorName;
   std::string m_obase;
-  char prompt[64];
+  std::vector<std::string> m_oargv;
 
-  short m_noper = 0;
-  bool m_isActive;
+#ifdef HAVE_LIBPTHREAD
+  pthread_t threadID;
+#endif
 
-  module_t m_module;
-  std::vector<std::string> m_oargv;
-  oper_t oper[MAX_OPERATOR];
+  /* Member Functions  */
 
-  Process(int p_ID, const std::string &p_operatorNamme, const std::vector<std::string> &operatorArguments);
+  Process(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module);
+  virtual ~Process() {}
 
-  pthread_t run();
-  ProcessStatus m_status = ProcessStatus::Ok;
+  pthread_t start_thread();
+  virtual void init() = 0;
+  virtual void run() = 0;
+  virtual void close() = 0;
 
   /**
    * returns the number of in streams this process currently has.
@@ -99,12 +87,6 @@ public:
    * Adds a Process as parent and adds the parents input stream to out streams.
    */
   void add_parent(const std::shared_ptr<Process> &parent_process);
-  /**
-   * Compares the wanted and current stream counts.
-   * @return if the wanted count is -1 this function always returns false.
-   * Are the current and wanted stream counts equal 1 and if they differ false.
-   */
-  bool has_hall_inputs();
   /**
    * Adds and creates a new file pstream to the in streams
    */
@@ -121,45 +103,35 @@ public:
    * Adds and creates a new file pstream to the out streams
    */
   void add_pipe_out_stream();
-  /**
-   * Adds an operator to the process
-   */
-  int operator_add(const char *name, int f1, int f2, const char *enter);
   /**
    * returns the operatorID of the currently in use operator
    */
   int get_operator_id();
-  void set_inactive();
   const char *inq_prompt() const;
-  void print_processed_values();
-  void print_benchmarks(double p_walltime, const char *p_memstring);
-  void check_stream_cnt();
-  void validate();
-  void handle_process_err();
 
+  const char *get_out_stream_name();
   bool has_out_stream(CdoStreamID p_streamPtr);
   bool has_in_stream(CdoStreamID p_streamPtr);
+
   bool has_no_pipes();
+
   size_t inq_nvals();
-  const char *get_out_stream_name();
+
   size_t get_oper_argc();
   std::string get_argv(int idx);
+
   const std::string get_obase();
-  void
-  set_obase(const std::string &obase)
-  {
-    m_obase = obase;
-  }  // TODO into cc
-  bool input_is_variable();
+  void set_obase(const std::string &obase);
+
   int get_id();
 
-  void init_process(const std::string &p_operatorName, const std::vector<std::string> &p_arguments);
   void close_streams();
 
-  Process(){};
+  std::string replace_alias(const std::string &p_calledBy, const CdoModule &p_module);
+  void def_prompt(const std::string &name);
 
-private:
-  void def_prompt();
+  void cdo_initialize();
+  void cdo_finish(void);
 };
 
 int get_process_num();
diff --git a/src/processManager.cc b/src/processManager.cc
index 1ea3922f1f708396867942d7bbd2251d0a7b4e53..03ef9c14bc97f82e3987262b1ccd09f5a358492f 100644
--- a/src/processManager.cc
+++ b/src/processManager.cc
@@ -10,6 +10,8 @@
 #include "cdo_output.h"
 #include "cdo_options.h"
 #include "fileStream.h"
+#include "factory.h"
+#include "util_string.h"
 
 #include <stack>
 #include <mutex>
@@ -20,23 +22,82 @@ static std::string parse_err_msg = "";
 
 static const int IS_OBASE = -1;
 
+extern "C" size_t getPeakRSS();
+static void
+get_max_memstring(char *p_memstring, size_t memstringLen)
+{
+  auto memmax = getPeakRSS();
+  if (memmax)
+    {
+      size_t muindex = 0;
+      const char *mu[] = { "B", "KB", "MB", "GB", "TB", "PB" };
+      size_t nmu = sizeof(mu) / sizeof(char *);
+      while (memmax > 9999 && muindex < nmu - 1)
+        {
+          memmax /= 1024;
+          muindex++;
+        }
+      std::snprintf(p_memstring, memstringLen, " %zu%s", memmax, mu[muindex]);
+    }
+}
+
+void
+print_benchmarks(double p_walltime, const char *p_memstring)
+{
+  auto numberOfUsedThreads = get_process_num();
+  if (Options::test)
+    fprintf(stdout, " [%.2fs%s %dthread%s]", p_walltime, p_memstring, numberOfUsedThreads, ADD_PLURAL(numberOfUsedThreads));
+  else
+    fprintf(stdout, " [%.2fs%s]", p_walltime, p_memstring);
+}
+
+void
+print_processed_values(Process *p_process, double p_time)
+{
+  set_text_color(stdout, GREEN);
+  fprintf(stdout, "%s: ", p_process->prompt);
+  reset_text_color(stdout);
+
+  auto nvals = p_process->inq_nvals();
+
+  int nvars = p_process->m_nvars;
+  if (nvals > 0)
+    {
+      fprintf(stdout, "Processed %zu value%s from %d variable%s", nvals, ADD_PLURAL(nvals), nvars, ADD_PLURAL(nvars));
+    }
+  else if (nvars > 0) { fprintf(stdout, "Processed %d variable%s", nvars, ADD_PLURAL(nvars)); }
+
+  int ntimesteps = p_process->ntimesteps;
+  if ((nvals || nvars) && ntimesteps > 0) fprintf(stdout, " over %d timestep%s", ntimesteps, ADD_PLURAL(ntimesteps));
+
+  if (p_process->m_ID == 0)
+    {
+      char memstring[32] = { "" };
+      get_max_memstring(memstring, sizeof(memstring));
+      print_benchmarks(p_time, memstring);
+    }
+
+  // if (m_nvars > 0 || nvals > 0 || ntimesteps > 0 || m_ID == 0) fprintf(stdout, ".");
+  fprintf(stdout, "\n");
+}
+
 void
 ProcessManager::buildProcessTree(std::vector<std::shared_ptr<Node>> roots)
 {
-  Debug(PROCESS,"Building process Tree");
+  Debug(PROCESS, "Building process Tree");
   std::shared_ptr<Node> node = roots[0]->isFile ? roots[0]->children[0] : roots[0];
 
   auto first_process = create_process(node->oper, split_args(node->arguments));
   if (node->numOut() == IS_OBASE)
     {
-      Debug(PROCESS,"Setting obase for %s", node->oper);
+      Debug(PROCESS, "Setting obase for %s", node->oper);
       first_process->set_obase(roots[0]->oper);
     }
   else if (node->numOut() > 0)
     {
       for (const auto &n : roots)
         {
-          Debug(PROCESS,"adding out files to %s", node->oper);
+          Debug(PROCESS, "adding out files to %s", node->oper);
           first_process->add_file_out_stream(n->oper);
         }
     }
@@ -45,7 +106,7 @@ ProcessManager::buildProcessTree(std::vector<std::shared_ptr<Node>> roots)
     {
       if (c->isFile)
         {
-          Debug(PROCESS,"Adding file in stream: %s", c->oper);
+          Debug(PROCESS, "Adding file in stream: %s", c->oper);
           first_process->add_file_in_stream(c->oper);
         }
       else
@@ -63,18 +124,18 @@ ProcessManager::buildProcessTree(std::vector<std::shared_ptr<Node>> roots)
 std::shared_ptr<Process>
 ProcessManager::build_node(std::shared_ptr<Node> parent_node)
 {
-  Debug(PROCESS,"Building process for %s", parent_node->oper);
+  Debug(PROCESS, "Building process for %s", parent_node->oper);
   auto parent_process = create_process(parent_node->oper, split_args(parent_node->arguments));
   for (auto child_node : parent_node->children)
     {
       if (child_node->isFile)
         {
-          Debug(PROCESS,"Adding file in stream: %s", child_node->oper);
+          Debug(PROCESS, "Adding file in stream: %s", child_node->oper);
           parent_process->add_file_in_stream(child_node->oper);
         }
       else
         {
-          Debug(PROCESS,"Building Process for %s", child_node->oper);
+          Debug(PROCESS, "Building Process for %s", child_node->oper);
           auto child_process = build_node(child_node);
           parent_process->add_child(child_process);
           child_process->add_parent(parent_process);
@@ -99,12 +160,17 @@ ProcessManager::run_processes()
               reset_text_color(stdout);
               fprintf(stdout, "Process started\n");
             }
-          m_threadIDs.push_back(idProcessPair.second->run());
+          m_threadIDs.push_back(idProcessPair.second->start_thread());
         }
     }
   m_threadIDs.push_back(pthread_self());
   // MpMO::PrintCerr(Green("%s: ") + "xProcess started", get_process_from_id(0).inq_prompt());
-  get_process_from_id(0)->m_module.func(get_process_from_id(0).get());
+  Process *process_zero = get_process_from_id(0).get();
+
+  double start = cdo::get_wtime();
+  execute(process_zero);
+  double end = cdo::get_wtime();
+  if (!Options::silentMode && (cdo::stdoutIsTerminal || Options::cdoVerbose)) print_processed_values(process_zero, end - start);
 }
 
 void
@@ -128,15 +194,22 @@ ProcessManager::clear_processes()
   m_numProcesses = 0;
   m_numProcessesActive = 0;
 }
+
 const std::shared_ptr<Process>
 ProcessManager::create_process(const std::string &operatorName, const std::vector<std::string> &arguments)
 {
+  std::shared_ptr<Process> new_process;
+  if ((m_numProcesses + 1) >= MAX_PROCESS) { cdo_abort("Limit of %d processes reached!", MAX_PROCESS); }
   auto processID = m_numProcesses++;
-  if (processID >= MAX_PROCESS) cdo_abort("Limit of %d processes reached!", MAX_PROCESS);
-  auto success = m_processes.insert(std::make_pair(processID, std::make_shared<Process>(processID, operatorName, arguments)));
-  if (!success.second) cdo_abort("Process %d could not be created", processID);
+
+  auto it = Factory::find(operatorName, [&processID]() { cdo_abort("Process %d could not be created", processID); });
+
+  auto constructor_function = Factory::get_constructor(it);
+  auto success = m_processes.insert(std::make_pair(processID, constructor_function(processID, operatorName, arguments)));
+  new_process = success.first->second;
   m_numProcessesActive++;
-  return success.first->second;
+
+  return new_process;
 }
 
 int
@@ -171,7 +244,7 @@ ProcessManager::get_operator_argv(std::string operatorArguments)
       while ((pos = operatorArguments.find(delimiter)) != std::string::npos)
         {
           argument_vector.push_back(operatorArguments.substr(0, pos));
-          Debug(PROCESS,"added argument %s", argument_vector.back());
+          Debug(PROCESS, "added argument %s", argument_vector.back());
           operatorArguments.erase(0, pos + 1);
         }
       argument_vector.push_back(operatorArguments);
@@ -190,10 +263,11 @@ ProcessManager::split_args(std::string operatorArguments)
   while ((pos = operatorArguments.find(delimiter)) != std::string::npos)
     {
       argument_vector.push_back(operatorArguments.substr(0, pos));
-      Debug(PROCESS,"added argument %s", argument_vector.back());
+      Debug(PROCESS, "added argument %s", argument_vector.back());
       operatorArguments.erase(0, pos + 1);
     }
   argument_vector.push_back(operatorArguments);
+  Debug(PROCESS, "added argument %s", argument_vector.back());
   return argument_vector;
 }
 
diff --git a/src/processManager.h b/src/processManager.h
index 237a8b89966c5e9d3fa126444f2abec2d71999c3..52638eb38eab017dfcbbd032e557e34c41847429 100644
--- a/src/processManager.h
+++ b/src/processManager.h
@@ -11,7 +11,6 @@
 
 #include <pthread.h>
 #include "node.h"
-#include "modules.h"
 
 // cdo includes
 
diff --git a/src/process_int.cc b/src/process_int.cc
index 3ac941302c746ee13b950d13a936c08d81d831c1..2b67fa3933fab39c50ffe70094103c9f48370c33 100644
--- a/src/process_int.cc
+++ b/src/process_int.cc
@@ -30,7 +30,12 @@
 #include "factory.h"
 
 thread_local Process *localProcess;
-static int NumProcessActive = 0;
+
+void
+set_local_process(Process *p)
+{
+  localProcess = p;
+}
 
 int
 cdo_filetype(void)
@@ -44,12 +49,6 @@ cdo_filetype(void)
   return CdoDefault::FileType;
 }
 
-Process &
-process_self(void)
-{
-  return *localProcess;
-}
-
 void
 process_def_var_num(int nvars)
 {
@@ -158,12 +157,6 @@ operator_input_arg(const char *enter)
     }
 }
 
-int
-cdo_operator_add(const char *name, int f1, int f2, const char *enter)
-{
-  return localProcess->operator_add(name, f1, f2, enter);
-}
-
 int
 cdo_operator_id(void)
 {
@@ -173,37 +166,37 @@ cdo_operator_id(void)
 int
 cdo_operator_f1(int operID)
 {
-  return localProcess->oper[operID].f1;
+  return localProcess->m_module.operators[operID].f1;
 }
 
 int
 cdo_operator_f2(int operID)
 {
-  return localProcess->oper[operID].f2;
+  return localProcess->m_module.operators[operID].f2;
 }
 
 const char *
 cdo_operator_name(int operID)
 {
-  return localProcess->oper[operID].name.c_str();
+  return localProcess->m_module.operators[operID].name.c_str();
 }
 
 std::string
 cdo_module_name()
 {
-  return get_module_name_to(cdo_operator_name(cdo_operator_id()));
+  return localProcess->m_module.name;
 }
 
 const char *
 cdo_operator_enter(int operID)
 {
-  return localProcess->oper[operID].enter;
+  return localProcess->m_module.operators[operID].enter;
 }
 
 int
 cdo_stream_number()
 {
-  return operator_stream_number(localProcess->operatorName);
+  return localProcess->m_module.get_number();
 }
 
 int
@@ -346,23 +339,6 @@ cdo_get_obase()
   return localProcess->get_obase();
 }
 
-void
-cdo_initialize(void *p_process)
-{
-#if defined(_OPENMP)
-  omp_set_num_threads(Threading::ompNumThreads);  // Has to be called for every module (pthread)!
-#endif
-  localProcess = (Process *) p_process;
-#ifdef HAVE_LIBPTHREAD
-  localProcess->threadID = pthread_self();
-#endif
-  Debug(PROCESS_INT, "Initializing process: %s (id: %d)", localProcess->operatorName, localProcess->get_id());
-
-#ifdef HAVE_LIBPTHREAD
-  Debug(PROCESS_INT, "process %d thread %ld", localProcess->m_ID, pthread_self());
-#endif
-}
-
 const char *
 process_inq_prompt(void)
 {
@@ -371,24 +347,6 @@ process_inq_prompt(void)
 
 extern "C" size_t getPeakRSS();
 
-void
-cdo_finish(void)
-{
-  assert(((void) "Internal error: module not initialized!", (localProcess != nullptr)));
-
-  Debug(PROCESS_INT, "Finishing process: %d", localProcess->m_ID);
-
-#ifdef HAVE_LIBPTHREAD
-  Debug(PROCESS_INT, "process %d thread %ld", localProcess->m_ID, pthread_self());
-#endif
-
-  if (!Options::silentMode && (cdo::stdoutIsTerminal || Options::cdoVerbose)) localProcess->print_processed_values();
-
-  // localProcess->close_streams();
-  localProcess->set_inactive();
-  NumProcessActive--;
-}
-
 /* TODO move cdoClose internals into Pstream::close/ CdoStream::close */
 void
 cdo_stream_close(CdoStreamID pstreamPtr)
@@ -421,34 +379,34 @@ cdo_stream_inq_vlist(CdoStreamID p_pstreamPtr)
 
 // - - - - - - -
 void
-cdo_write_record_f(CdoStreamID p_pstreamPtr, float *data, size_t nmiss)
+cdo_write_record_f(CdoStreamID p_pstreamPtr, float *data, size_t numMissVals)
 {
-  p_pstreamPtr->write_record(data, nmiss);
+  p_pstreamPtr->write_record(data, numMissVals);
 }
 
 void
-cdo_write_record(CdoStreamID p_pstreamPtr, double *data, size_t nmiss)
+cdo_write_record(CdoStreamID p_pstreamPtr, double *data, size_t numMissVals)
 {
-  p_pstreamPtr->write_record(data, nmiss);
+  p_pstreamPtr->write_record(data, numMissVals);
 }
 
 void
 cdo_write_record(CdoStreamID p_pstreamPtr, Field &field)
 {
   if (field.memType == MemType::Float)
-    cdo_write_record_f(p_pstreamPtr, field.vec_f.data(), field.nmiss);
+    cdo_write_record_f(p_pstreamPtr, field.vec_f.data(), field.numMissVals);
   else
-    cdo_write_record(p_pstreamPtr, field.vec_d.data(), field.nmiss);
+    cdo_write_record(p_pstreamPtr, field.vec_d.data(), field.numMissVals);
 }
 
 void
-cdo_write_record(CdoStreamID p_pstreamPtr, Field3D &field, int levelID, size_t nmiss)
+cdo_write_record(CdoStreamID p_pstreamPtr, Field3D &field, int levelID, size_t numMissVals)
 {
   const auto offset = levelID * field.gridsize * field.nwpv;
   if (field.memType == MemType::Float)
-    cdo_write_record_f(p_pstreamPtr, field.vec_f.data() + offset, nmiss);
+    cdo_write_record_f(p_pstreamPtr, field.vec_f.data() + offset, numMissVals);
   else
-    cdo_write_record(p_pstreamPtr, field.vec_d.data() + offset, nmiss);
+    cdo_write_record(p_pstreamPtr, field.vec_d.data() + offset, numMissVals);
 }
 // - - - - - - -
 void
@@ -485,36 +443,36 @@ cdo_inq_byteorder(CdoStreamID p_pstreamPtr)
 
 // - - - - - - -
 void
-cdo_read_record_f(CdoStreamID p_pstreamPtr, float *data, size_t *nmiss)
+cdo_read_record_f(CdoStreamID p_pstreamPtr, float *data, size_t *numMissVals)
 {
   if (data == nullptr) cdo_abort("Data pointer not allocated (cdo_read_record)!");
-  p_pstreamPtr->read_record(data, nmiss);
+  p_pstreamPtr->read_record(data, numMissVals);
 }
 
 void
-cdo_read_record(CdoStreamID p_pstreamPtr, double *data, size_t *nmiss)
+cdo_read_record(CdoStreamID p_pstreamPtr, double *data, size_t *numMissVals)
 {
   if (data == nullptr) cdo_abort("Data pointer not allocated (cdo_read_record)!");
-  p_pstreamPtr->read_record(data, nmiss);
+  p_pstreamPtr->read_record(data, numMissVals);
 }
 
 void
 cdo_read_record(CdoStreamID streamID, Field &field)
 {
   if (field.memType == MemType::Float)
-    cdo_read_record_f(streamID, field.vec_f.data(), &field.nmiss);
+    cdo_read_record_f(streamID, field.vec_f.data(), &field.numMissVals);
   else
-    cdo_read_record(streamID, field.vec_d.data(), &field.nmiss);
+    cdo_read_record(streamID, field.vec_d.data(), &field.numMissVals);
 }
 
 void
-cdo_read_record(CdoStreamID streamID, Field3D &field, int levelID, size_t *nmiss)
+cdo_read_record(CdoStreamID streamID, Field3D &field, int levelID, size_t *numMissVals)
 {
   const auto offset = levelID * field.gridsize * field.nwpv;
   if (field.memType == MemType::Float)
-    cdo_read_record_f(streamID, field.vec_f.data() + offset, nmiss);
+    cdo_read_record_f(streamID, field.vec_f.data() + offset, numMissVals);
   else
-    cdo_read_record(streamID, field.vec_d.data() + offset, nmiss);
+    cdo_read_record(streamID, field.vec_d.data() + offset, numMissVals);
 }
 // - - - - - - -
 
diff --git a/src/process_int.h b/src/process_int.h
index 7c01d5936ba5135af18f25d1287d7ee303a8d85e..ee4a7ada7649ddebffb4cd3f1cc48030de04500b 100644
--- a/src/process_int.h
+++ b/src/process_int.h
@@ -8,12 +8,11 @@
 #ifndef PROCESS_INT_H
 #define PROCESS_INT_H
 
-#include "processManager.h"
 #include "cdo_cdi_wrapper.h"
 #include "process.h"
 #include "factory.h"
 
-Process &process_self(void);
+void set_local_process(Process *p);
 bool cdo_assert_files_only();
 bool cdo_stream_is_pipe(CdoStreamID streamID);
 bool data_is_unchanged();
@@ -43,15 +42,15 @@ void cdo_set_nan(double missval, size_t gridsize, double *array);
 void cdo_inq_record(CdoStreamID streamID, int *varID, int *levelID);
 void cdo_def_record(CdoStreamID streamID, int varID, int levelID);
 
-void cdo_read_record_f(CdoStreamID streamID, float *data, size_t *nmiss);
-void cdo_read_record(CdoStreamID streamID, double *data, size_t *nmiss);
+void cdo_read_record_f(CdoStreamID streamID, float *data, size_t *numMissVals);
+void cdo_read_record(CdoStreamID streamID, double *data, size_t *numMissVals);
 void cdo_read_record(CdoStreamID streamID, Field &field);
-void cdo_read_record(CdoStreamID streamID, Field3D &field, int levelID, size_t *nmiss);
+void cdo_read_record(CdoStreamID streamID, Field3D &field, int levelID, size_t *numMissVals);
 
-void cdo_write_record_f(CdoStreamID streamID, float *data, size_t nmiss);
-void cdo_write_record(CdoStreamID streamID, double *data, size_t nmiss);
+void cdo_write_record_f(CdoStreamID streamID, float *data, size_t numMissVals);
+void cdo_write_record(CdoStreamID streamID, double *data, size_t numMissVals);
 void cdo_write_record(CdoStreamID streamID, Field &data);
-void cdo_write_record(CdoStreamID streamID, Field3D &data, int levelID, size_t nmiss);
+void cdo_write_record(CdoStreamID streamID, Field3D &data, int levelID, size_t numMissVals);
 
 void cdo_copy_record(CdoStreamID streamIDdest, CdoStreamID streamIDsrc);
 
@@ -70,11 +69,14 @@ CdoStreamID cdo_open_append(int outStreamIDX);
 CdoStreamID cdo_open_read(int inStreamIDX);
 CdoStreamID cdo_open_write(int outStreamIDX, int filetype = CDI_UNDEFID);
 CdoStreamID cdo_open_write(const std::string &p_filename, int filetype = CDI_UNDEFID);
-int cdo_operator_add(const char *name, int f1, int f2, const char *enter);
 int cdo_operator_f1(int operID);
 int cdo_operator_f2(int operID);
 int cdo_operator_id(void);
-void cdo_finish();
-void cdo_initialize(void *process);
+
+static inline bool
+stream_is_pipe(int p_streamIndex)
+{
+  return (strncmp(cdo_get_stream_name(p_streamIndex), "(pipe", 5) == 0);
+}
 
 #endif
diff --git a/src/pthread_debug.h b/src/pthread_debug.h
index b208f741083cafc40111dbabd06ed42ef5ab36a2..1990f46c261b229c8285e4095b0c0edafc822568 100644
--- a/src/pthread_debug.h
+++ b/src/pthread_debug.h
@@ -7,6 +7,8 @@
 #ifndef PTHREAD_DEBUG_H
 #define PTHREAD_DEBUG_H
 
+#include <pthread.h>
+
 void print_pthread_attr(const char *caller, pthread_attr_t *attr);
 
 int Pthread_create(const char *caller, pthread_t *th, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
diff --git a/src/readline.cc b/src/readline.cc
deleted file mode 100644
index bbc155a1cf53e629fc85fedc6bf44ada0e3edf46..0000000000000000000000000000000000000000
--- a/src/readline.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
-
-  Author: Uwe Schulzweida
-
-*/
-
-#include "readline.h"
-
-namespace cdo
-{
-
-int
-readline(FILE *fp, char *line, int len)
-{
-  int ichar, ipos = 0;
-
-  while ((ichar = fgetc(fp)) != EOF)
-    {
-      if (ichar == '\r')
-        {
-          if ((ichar = fgetc(fp)) != EOF)
-            if (ichar != '\n') ungetc(ichar, fp);
-          break;
-        }
-      if (ichar == '\n') break;
-      line[ipos++] = ichar;
-      if (ipos >= len)
-        {
-          fprintf(stderr, "readline Warning: end of line not found (maxlen = %d)!\n", len);
-          break;
-        }
-    }
-  line[ipos] = 0;
-
-  if (feof(fp) && ipos == 0) return 0;
-
-  return 1;
-}
-
-}  // namespace cdo
diff --git a/src/readline.h b/src/readline.h
deleted file mode 100644
index 2c339e06519b2d5b5aa44af901bb9787e1e8f1fc..0000000000000000000000000000000000000000
--- a/src/readline.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
-
-  Author: Uwe Schulzweida
-
-*/
-#ifndef READLINE_H
-#define READLINE_H
-
-#include <cstdio>
-
-namespace cdo
-{
-
-int readline(FILE *fp, char *line, int len);
-
-}
-
-#endif
diff --git a/src/region.cc b/src/region.cc
index 866fff393a762901eb9eef5b0dae9c3e2e60f168..ac5db65af588942dc13d2d0987750b277dfc3f89 100644
--- a/src/region.cc
+++ b/src/region.cc
@@ -5,23 +5,23 @@
 
 */
 
+#include <fstream>
+
 #include "cdo_options.h"
 #include "cdo_output.h"
 #include "util_string.h"
 #include "varray.h"
-#include "readline.h"
 #include "dcw_reader.h"
 #include "region.h"
 
 static int
-read_coords(size_t segmentNo, Varray<double> &xvals, Varray<double> &yvals, const char *polyfile, FILE *fp)
+read_coords(size_t segmentNo, Varray<double> &xvals, Varray<double> &yvals, const std::string &polyfile, std::ifstream &file)
 {
   auto maxVals = xvals.size();
-  constexpr size_t MAX_LINE = 256;
-  char line[MAX_LINE];
 
   size_t number = 0, jumpedlines = 0;
-  while (cdo::readline(fp, line, MAX_LINE))
+  std::string line;
+  while (std::getline(file, line))
     {
       if (line[0] == '#' || line[0] == '\0')
         {
@@ -35,7 +35,7 @@ read_coords(size_t segmentNo, Varray<double> &xvals, Varray<double> &yvals, cons
       auto lineNo = number + jumpedlines + 1;
 
       double xcoord, ycoord;
-      auto nread = std::sscanf(line, "%lf %lf", &xcoord, &ycoord);
+      auto nread = std::sscanf(line.c_str(), "%lf %lf", &xcoord, &ycoord);
       if (nread != 2)
         {
           if (Options::cdoVerbose) cdo_print("nread=%zu, xcoord=%g, ycoord=%g, line=%s\n", nread, xcoord, ycoord, line);
@@ -62,10 +62,10 @@ read_coords(size_t segmentNo, Varray<double> &xvals, Varray<double> &yvals, cons
 }
 
 void
-read_regions_from_file(const char *filename, Regions &regions)
+read_regions_from_file(const std::string &filename, Regions &regions)
 {
-  auto fp = std::fopen(filename, "r");
-  if (fp == nullptr) cdo_abort("Open failed on %s", filename);
+  std::ifstream file(filename);
+  if (!file.is_open()) cdo_abort("Open failed on: %s\n", filename);
 
   constexpr size_t maxVals = 1048576;
   Varray<double> xcoords(maxVals), ycoords(maxVals);
@@ -74,7 +74,7 @@ read_regions_from_file(const char *filename, Regions &regions)
   size_t n = 0;
   while (true)
     {
-      auto segmentSize = read_coords(segmentNo++, xcoords, ycoords, filename, fp);
+      auto segmentSize = read_coords(segmentNo++, xcoords, ycoords, filename, file);
       if (segmentSize == 0) break;
       if (segmentSize < 3) cdo_abort("Too few point for polygon in file %s (Min=3)!", filename);
 
@@ -91,7 +91,7 @@ read_regions_from_file(const char *filename, Regions &regions)
       for (int i = 0; i < segmentSize; ++i) y[offset + i] = ycoords[i];
     }
 
-  std::fclose(fp);
+  file.close();
 }
 
 void
diff --git a/src/region.h b/src/region.h
index b7743da839374b515bc668c33c4b19169114d2d7..5891fa6e6d76b528ef39e88fabc6deb3e4e62c39 100644
--- a/src/region.h
+++ b/src/region.h
@@ -7,6 +7,10 @@
 #ifndef REGION_H
 #define REGION_H
 
+#include <vector>
+#include <cstddef> // size_t
+#include <string>
+
 struct Regions
 {
   std::vector<double> x, y;
@@ -15,7 +19,7 @@ struct Regions
   size_t numSegments = 0;
 };
 
-void read_regions_from_file(const char *filename, Regions &regions);
+void read_regions_from_file(const std::string &filename, Regions &regions);
 void read_regions_from_dcw(const char *codeNames, Regions &regions);
 
 #endif  //  REGION_H
diff --git a/src/remap.h b/src/remap.h
index 86af5cb652c640532472a4b8905062fd82d78b13..4ca62eea9b3407eccf47646fcfc44273cda71048 100644
--- a/src/remap.h
+++ b/src/remap.h
@@ -136,7 +136,7 @@ RemapType
   int nused = 0;
   int gridID = -1;
   size_t gridsize = 0;
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   RemapGrid srcGrid;
   RemapGrid tgtGrid;
   RemapVars vars;
@@ -185,8 +185,8 @@ void remap_gradients(RemapGrid &grid, const Field &field, RemapGradients &gradie
 void sort_add(size_t numLinks, size_t numWeights, size_t *add1, size_t *add2, double *weights);
 void sort_iter(size_t numLinks, size_t numWeights, size_t *add1, size_t *add2, double *weights, int parent);
 
-void remap_write_data_scrip(const std::string &weightsfile, const RemapSwitches &remapSwitches, RemapGrid &srcGrid, RemapGrid &tgtGrid,
-                            RemapVars &rv);
+void remap_write_data_scrip(const std::string &weightsfile, const RemapSwitches &remapSwitches, RemapGrid &srcGrid,
+                            RemapGrid &tgtGrid, RemapVars &rv);
 RemapSwitches remap_read_data_scrip(const std::string &weightsfile, int gridID1, int gridID2, RemapGrid &srcGrid,
                                     RemapGrid &tgtGrid, RemapVars &rv);
 
@@ -217,11 +217,11 @@ void remap_check_area(size_t grid_size, const Varray<double> &cell_area, const c
 
 template <typename T>
 void
-remap_set_mask(size_t gridsize, const Varray<T> &v, size_t nmiss, T missval, Varray<short> &mask)
+remap_set_mask(size_t gridsize, const Varray<T> &v, size_t numMissVals, T missval, Varray<short> &mask)
 {
   mask.resize(gridsize);
 
-  if (nmiss)
+  if (numMissVals)
     {
       if (std::isnan(missval))
         {
@@ -245,12 +245,12 @@ remap_set_mask(size_t gridsize, const Varray<T> &v, size_t nmiss, T missval, Var
 }
 
 inline void
-remap_set_mask(size_t gridsize, const Field &field1, size_t nmiss, double missval, Varray<short> &imask)
+remap_set_mask(size_t gridsize, const Field &field1, size_t numMissVals, double missval, Varray<short> &imask)
 {
   if (field1.memType == MemType::Float)
-    remap_set_mask(gridsize, field1.vec_f, nmiss, (float) missval, imask);
+    remap_set_mask(gridsize, field1.vec_f, numMissVals, (float) missval, imask);
   else
-    remap_set_mask(gridsize, field1.vec_d, nmiss, missval, imask);
+    remap_set_mask(gridsize, field1.vec_d, numMissVals, missval, imask);
 }
 
 #endif /* REMAP_H */
diff --git a/src/remap_bicubic.cc b/src/remap_bicubic.cc
index 8c1b8e6b4a0040cef6779ba82b5b23fbbb6f820d..bf8826af263a920de6c249abf4ca40c4db4c9778 100644
--- a/src/remap_bicubic.cc
+++ b/src/remap_bicubic.cc
@@ -6,7 +6,6 @@
 */
 
 #include <atomic>
-#include <algorithm>  // std::sort
 
 #include "process_int.h"
 #include "cdo_timer.h"
@@ -82,8 +81,7 @@ bicubic_sort_weights(size_t (&indices)[4], double (&weights)[4][4])
       for (int k = 0; k < 4; ++k) indexWeights[i].weight[k] = weights[i][k];
     }
 
-  auto comp_index = [](const auto &a, const auto &b) noexcept { return a.index < b.index; };
-  std::sort(indexWeights.begin(), indexWeights.end(), comp_index);
+  ranges::sort(indexWeights, {}, &IndexWeightX::index);
 
   for (size_t i = 0; i < numWeights; ++i)
     {
@@ -228,7 +226,7 @@ bicubic_remap(const Varray<T> &srcArray, const double (&weights)[4][4], const si
 
 template <typename T>
 static void
-remap_bicubic(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t nmiss)
+remap_bicubic(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t numMissVals)
 {
   auto srcGrid = rsearch.srcGrid;
   auto tgtGrid = rsearch.tgtGrid;
@@ -245,7 +243,7 @@ remap_bicubic(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArr
   auto srcGridSize = srcGrid->size;
 
   Varray<short> srcGridMask;
-  if (nmiss) remap_set_mask(srcGridSize, srcArray, nmiss, missval, srcGridMask);
+  if (numMissVals) remap_set_mask(srcGridSize, srcArray, numMissVals, missval, srcGridMask);
 
   // Compute mappings from source to target grid
 
@@ -323,7 +321,7 @@ remap_bicubic(RemapSearch &rsearch, const Field &field1, Field &field2)
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    remap_bicubic(rsearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.nmiss);
+    remap_bicubic(rsearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.numMissVals);
   else
-    remap_bicubic(rsearch, field1.vec_d, field2.vec_d, field1.missval, field1.nmiss);
+    remap_bicubic(rsearch, field1.vec_d, field2.vec_d, field1.missval, field1.numMissVals);
 }
diff --git a/src/remap_bilinear.cc b/src/remap_bilinear.cc
index cb123f2bd07b49a3bd0f5ba236f0882f76629618..011ec125f2aab9dc65e8dfff8b65977baee2e0e8 100644
--- a/src/remap_bilinear.cc
+++ b/src/remap_bilinear.cc
@@ -6,7 +6,6 @@
 */
 
 #include <atomic>
-#include <algorithm>  // std::sort
 
 #include "process_int.h"
 #include "cdo_timer.h"
@@ -142,8 +141,7 @@ bilinear_sort_weights(size_t (&indices)[4], double (&weights)[4])
       indexWeights[i].weight = weights[i];
     }
 
-  auto comp_index = [](const auto &a, const auto &b) noexcept { return a.index < b.index; };
-  std::sort(indexWeights.begin(), indexWeights.end(), comp_index);
+  ranges::sort(indexWeights, {}, &IndexWeightX::index);
 
   for (size_t i = 0; i < numWeights; ++i)
     {
@@ -379,7 +377,7 @@ remap_bilinear_healpix(const RemapSearch &rsearch, const Varray<T> &srcArray, co
 // This routine computes and apply the weights for a bilinear interpolation.
 template <typename T>
 static void
-remap_bilinear(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t nmiss)
+remap_bilinear(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t numMissVals)
 {
   const auto srcGrid = rsearch.srcGrid;
   auto tgtGrid = rsearch.tgtGrid;
@@ -399,7 +397,7 @@ remap_bilinear(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtAr
   auto srcGridSize = srcGrid->size;
 
   Varray<short> srcGridMask;
-  if (nmiss) remap_set_mask(srcGridSize, srcArray, nmiss, missval, srcGridMask);
+  if (numMissVals) remap_set_mask(srcGridSize, srcArray, numMissVals, missval, srcGridMask);
 
   // Compute mappings from source to target grid
 
@@ -439,7 +437,7 @@ remap_bilinear(RemapSearch &rsearch, const Field &field1, Field &field2)
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    remap_bilinear(rsearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.nmiss);
+    remap_bilinear(rsearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.numMissVals);
   else
-    remap_bilinear(rsearch, field1.vec_d, field2.vec_d, field1.missval, field1.nmiss);
+    remap_bilinear(rsearch, field1.vec_d, field2.vec_d, field1.missval, field1.numMissVals);
 }
diff --git a/src/remap_conserv.cc b/src/remap_conserv.cc
index c7c903a6b6578881e14ca68f4bf3b81c311ad1cd..5c0c0bfc30a9193642ebe9b5a9517a2c74d83de3 100644
--- a/src/remap_conserv.cc
+++ b/src/remap_conserv.cc
@@ -6,7 +6,6 @@
 */
 
 #include <atomic>
-#include <algorithm>  // std::sort
 
 #include "process_int.h"
 #include "cdo_timer.h"
@@ -147,7 +146,7 @@ static double
 get_edge_direction(const double *ref_corner, double *corner_a, double *corner_b)
 {
   double edge_norm[3];
-  crossproduct_ld(corner_a, corner_b, edge_norm);
+  crossproduct_kahan(corner_a, corner_b, edge_norm);
   normalise_vector(edge_norm);
 
   // sine of the angle between the edge and the reference corner
@@ -174,9 +173,7 @@ cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, con
   // the triangulation algorithm only works for cells that only have great circle edges
   enum yac_edge_type edgeTypes[3] = { GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE, GREAT_CIRCLE_EDGE };
   double coordinates_xyz[3][3] = { { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 } };
-  coordinates_xyz[0][0] = baseCorner[0];
-  coordinates_xyz[0][1] = baseCorner[1];
-  coordinates_xyz[0][2] = baseCorner[2];
+  for (int k = 0; k < 3; ++k) coordinates_xyz[0][k] = baseCorner[k];
 
   // data structure to hold the triangles of the target cell
   struct grid_cell partialCell;
@@ -203,12 +200,8 @@ cdo_compute_concave_overlap_areas(size_t numSearchCells, CellSearch &search, con
 
       auto edgeDirection = get_edge_direction(baseCorner, cornerA, cornerB);
 
-      partialCell.coordinates_xyz[1][0] = cornerA[0];
-      partialCell.coordinates_xyz[1][1] = cornerA[1];
-      partialCell.coordinates_xyz[1][2] = cornerA[2];
-      partialCell.coordinates_xyz[2][0] = cornerB[0];
-      partialCell.coordinates_xyz[2][1] = cornerB[1];
-      partialCell.coordinates_xyz[2][2] = cornerB[2];
+      for (int k = 0; k < 3; ++k) partialCell.coordinates_xyz[1][k] = cornerA[k];
+      for (int k = 0; k < 3; ++k) partialCell.coordinates_xyz[2][k] = cornerB[k];
 
       // clip the current target cell triangle with all source cells
       yac_cell_clipping(numSearchCells, sourceCells.data(), partialCell, overlapCells.data());
@@ -410,9 +403,7 @@ normalize_weights(RemapGrid *tgtGrid, RemapVars &rv)
           rv.weights[i * numWeights] *= normFactor;
         }
     }
-  else if (rv.normOpt == NormOpt::NONE)
-    {
-    }
+  else if (rv.normOpt == NormOpt::NONE) {}
 }
 
 static void
@@ -428,9 +419,7 @@ normalize_weights(NormOpt normOpt, double cellArea, double cellFrac, size_t numW
       auto normFactor = is_not_equal(cellFrac, 0.0) ? 1.0 / cellFrac : 0.0;
       for (size_t i = 0; i < numWeights; ++i) weights[i] *= normFactor;
     }
-  else if (normOpt == NormOpt::NONE)
-    {
-    }
+  else if (normOpt == NormOpt::NONE) {}
 }
 
 static void
@@ -462,8 +451,7 @@ sort_weights(size_t numWeights, Varray<size_t> &indices, Varray<double> &weights
       indexWeights[i].weight = weights[i];
     }
 
-  auto compareIndex = [](const auto &a, const auto &b) noexcept { return a.index < b.index; };
-  std::sort(indexWeights.begin(), indexWeights.end(), compareIndex);
+  ranges::sort(indexWeights, {}, &IndexWeightX::index);
 
   for (size_t i = 0; i < numWeights; ++i)
     {
@@ -649,7 +637,7 @@ remap_conserv_weights(RemapSearch &remapSearch, RemapVars &rv)
   if (!useCellsearch)
     for (int i = 0; i < Threading::ompNumThreads; ++i) indices2[i].resize(srcGridSize);
 
-  // Loop over target grid
+      // Loop over target grid
 
 #ifdef _OPENMP
 #pragma omp parallel for schedule(dynamic) default(shared)
@@ -734,8 +722,10 @@ remap_conserv_weights(RemapSearch &remapSearch, RemapVars &rv)
 
       if (numWeights > 0)
         {
-          if (linksPerValue == -1) linksPerValue = numWeights;
-          else if (linksPerValue > 0 && linksPerValue != (long)numWeights) linksPerValue = 0;
+          if (linksPerValue == -1)
+            linksPerValue = numWeights;
+          else if (linksPerValue > 0 && linksPerValue != (long) numWeights)
+            linksPerValue = 0;
         }
     }
 
@@ -790,7 +780,7 @@ conserv_remap(const Varray<T> &srcArray, size_t numWeights, const Varray<double>
 
 template <typename T>
 static void
-remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t nmiss)
+remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t numMissVals)
 {
   auto srcGrid = remapSearch.srcGrid;
   auto tgtGrid = remapSearch.tgtGrid;
@@ -811,7 +801,7 @@ remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArr
   auto tgtGridSize = tgtGrid->size;
 
   Varray<short> srcGridMask;
-  if (nmiss) remap_set_mask(srcGridSize, srcArray, nmiss, missval, srcGridMask);
+  if (numMissVals) remap_set_mask(srcGridSize, srcArray, numMissVals, missval, srcGridMask);
 
   auto srcNumCorners = srcGrid->num_cell_corners;
   auto tgtNumCorners = tgtGrid->num_cell_corners;
@@ -866,7 +856,7 @@ remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArr
   if (!useCellsearch)
     for (int i = 0; i < Threading::ompNumThreads; ++i) indices2[i].resize(srcGridSize);
 
-  // Loop over target grid
+      // Loop over target grid
 
 #ifdef _OPENMP
 #pragma omp parallel for schedule(dynamic) default(shared)
@@ -981,9 +971,9 @@ remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Field &field1, Fi
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    remap_conserv(normOpt, remapSearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.nmiss);
+    remap_conserv(normOpt, remapSearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.numMissVals);
   else
-    remap_conserv(normOpt, remapSearch, field1.vec_d, field2.vec_d, field1.missval, field1.nmiss);
+    remap_conserv(normOpt, remapSearch, field1.vec_d, field2.vec_d, field1.missval, field1.numMissVals);
 }
 
 template <typename T>
@@ -1182,11 +1172,11 @@ remap_zonal_mean(const Varray2D<size_t> &remapIndices, const Varray2D<double> &r
         }
     }
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
   for (size_t i2 = 0; i2 < ysize2; ++i2)
-    if (dbl_is_equal(tgtArray[i2], missval)) nmiss++;
+    if (dbl_is_equal(tgtArray[i2], missval)) numMissVals++;
 
-  return nmiss;
+  return numMissVals;
 }
 
 void
@@ -1194,12 +1184,12 @@ remap_zonal_mean(const Varray2D<size_t> &remapIndices, const Varray2D<double> &r
 {
   // clang-format off
   if      (field1.memType == MemType::Float && field2.memType == MemType::Float)
-    field2.nmiss = remap_zonal_mean(remapIndices, remapWeights, field1.vec_f, field2.vec_f, (float)field1.missval);
+    field2.numMissVals = remap_zonal_mean(remapIndices, remapWeights, field1.vec_f, field2.vec_f, (float)field1.missval);
   else if (field1.memType == MemType::Float && field2.memType == MemType::Double)
-    field2.nmiss = remap_zonal_mean(remapIndices, remapWeights, field1.vec_f, field2.vec_d, (float)field1.missval);
+    field2.numMissVals = remap_zonal_mean(remapIndices, remapWeights, field1.vec_f, field2.vec_d, (float)field1.missval);
   else if (field1.memType == MemType::Double && field2.memType == MemType::Float)
-    field2.nmiss = remap_zonal_mean(remapIndices, remapWeights, field1.vec_d, field2.vec_f, field1.missval);
+    field2.numMissVals = remap_zonal_mean(remapIndices, remapWeights, field1.vec_d, field2.vec_f, field1.missval);
   else
-    field2.nmiss = remap_zonal_mean(remapIndices, remapWeights, field1.vec_d, field2.vec_d, field1.missval);
+    field2.numMissVals = remap_zonal_mean(remapIndices, remapWeights, field1.vec_d, field2.vec_d, field1.missval);
   // clang-format on
 }
diff --git a/src/remap_conserv_scrip.cc b/src/remap_conserv_scrip.cc
index f903046bf422c90e367fb0ad3a2ae428cdfd9889..e1f04913b40b8d51dfe5b5d6c5b5e4d2db66fb44 100644
--- a/src/remap_conserv_scrip.cc
+++ b/src/remap_conserv_scrip.cc
@@ -1280,7 +1280,7 @@ remap_conserv_weights_scrip(RemapSearch &rsearch, RemapVars &rv)
   srch_corners = tgtNumCorners;
 
 #ifdef _OPENMP
-#pragma omp parallel for default(none) shared(findex, rsearch, numWeights, src_centroid_lon, src_centroid_lat, grid_store, rv,      \
+#pragma omp parallel for default(none) shared(findex, rsearch, numWeights, src_centroid_lon, src_centroid_lat, grid_store, rv,   \
                                               Options::cdoVerbose, max_subseg, srch_corner_lat, srch_corner_lon, maxSearchCells, \
                                               srcNumCorners, srch_corners, srcGrid, tgtGrid, tgtGridSize, srcGridSize, srch_add)
 #endif
@@ -1467,7 +1467,7 @@ remap_conserv_weights_scrip(RemapSearch &rsearch, RemapVars &rv)
   findex = 0;
 
 #ifdef _OPENMP
-#pragma omp parallel for default(none) shared(findex, rsearch, numWeights, tgt_centroid_lon, tgt_centroid_lat, grid_store, rv,      \
+#pragma omp parallel for default(none) shared(findex, rsearch, numWeights, tgt_centroid_lon, tgt_centroid_lat, grid_store, rv,   \
                                               Options::cdoVerbose, max_subseg, srch_corner_lat, srch_corner_lon, maxSearchCells, \
                                               tgtNumCorners, srch_corners, srcGrid, tgtGrid, tgtGridSize, srcGridSize, srch_add)
 #endif
diff --git a/src/remap_distwgt.cc b/src/remap_distwgt.cc
index 7d96a85c4b9ffebc9135fe71aefd4f9b8818484e..c826fc6d9e1eec8d9e73d9ec33766ea978925e6c 100644
--- a/src/remap_distwgt.cc
+++ b/src/remap_distwgt.cc
@@ -77,8 +77,10 @@ remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
 
       if (numNeighbors > 1 && nadds > 0)
         {
-          if (linksPerValue == -1) linksPerValue = nadds;
-          else if (linksPerValue > 0 && linksPerValue != (long)nadds) linksPerValue = 0;
+          if (linksPerValue == -1)
+            linksPerValue = nadds;
+          else if (linksPerValue > 0 && linksPerValue != (long) nadds)
+            linksPerValue = 0;
         }
     }
 
@@ -88,15 +90,17 @@ remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
 
   weight_links_to_remap_links(0, tgtGridSize, weightLinks, rv);
 
-  if (numNeighbors == 1) rv.linksPerValue = 1;
-  else if (linksPerValue > 0) rv.linksPerValue = linksPerValue;
+  if (numNeighbors == 1)
+    rv.linksPerValue = 1;
+  else if (linksPerValue > 0)
+    rv.linksPerValue = linksPerValue;
 
   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds", timer.elapsed());
 }  // remap_distwgt_weights
 
 template <typename T>
 static void
-remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t nmiss)
+remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t numMissVals)
 {
   auto srcGrid = rsearch.srcGrid;
   auto tgtGrid = rsearch.tgtGrid;
@@ -111,7 +115,7 @@ remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArr
   auto srcGridSize = srcGrid->size;
 
   Varray<short> srcGridMask;
-  if (nmiss) remap_set_mask(srcGridSize, srcArray, nmiss, missval, srcGridMask);
+  if (numMissVals) remap_set_mask(srcGridSize, srcArray, numMissVals, missval, srcGridMask);
 
   std::vector<knnWeightsType> knnWeights;
   knnWeights.reserve(Threading::ompNumThreads);
@@ -145,8 +149,7 @@ remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArr
       remap_search_points(rsearch, llpoint, knnWgt);
 
       // Compute weights based on inverse distance if mask is false, eliminate those points
-      auto nadds
-          = (srcGridMask.size() > 0) ? knnWgt.computeWeights(srcGridMask) : knnWgt.computeWeights();
+      auto nadds = (srcGridMask.size() > 0) ? knnWgt.computeWeights(srcGridMask) : knnWgt.computeWeights();
       if (nadds) tgtValue = knnWgt.arrayWeightsSum(srcArray);
     }
 
@@ -161,9 +164,9 @@ remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Field &field1, Fi
   if (field1.memType != field2.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
 
   if (field1.memType == MemType::Float)
-    remap_distwgt(numNeighbors, rsearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.nmiss);
+    remap_distwgt(numNeighbors, rsearch, field1.vec_f, field2.vec_f, (float) field1.missval, field1.numMissVals);
   else
-    remap_distwgt(numNeighbors, rsearch, field1.vec_d, field2.vec_d, field1.missval, field1.nmiss);
+    remap_distwgt(numNeighbors, rsearch, field1.vec_d, field2.vec_d, field1.missval, field1.numMissVals);
 }
 
 void
@@ -193,7 +196,7 @@ intgriddis(const Field &field1, Field &field2, size_t numNeighbors)
   auto tgtGridSize = remap.tgtGrid.size;
 
   Varray<short> srcGridMask;
-  if (field1.nmiss) remap_set_mask(srcGridSize, srcArray, field1.nmiss, srcMissval, srcGridMask);
+  if (field1.numMissVals) remap_set_mask(srcGridSize, srcArray, field1.numMissVals, srcMissval, srcGridMask);
 
   std::vector<knnWeightsType> knnWeights;
   knnWeights.reserve(Threading::ompNumThreads);
@@ -205,7 +208,7 @@ intgriddis(const Field &field1, Field &field2, size_t numNeighbors)
 
   // Loop over target grid
 
-  std::atomic<size_t> nmiss{ 0 };
+  std::atomic<size_t> numMissVals{ 0 };
   std::atomic<size_t> atomicCount{ 0 };
 
 #ifdef _OPENMP
@@ -230,17 +233,16 @@ intgriddis(const Field &field1, Field &field2, size_t numNeighbors)
       remap_search_points(remap.search, llpoint, knnWgt);
 
       // Compute weights based on inverse distance if mask is false, eliminate those points
-      auto nadds
-          = (srcGridMask.size() > 0) ? knnWgt.computeWeights(srcGridMask) : knnWgt.computeWeights();
+      auto nadds = (srcGridMask.size() > 0) ? knnWgt.computeWeights(srcGridMask) : knnWgt.computeWeights();
       if (nadds)
         tgtValue = knnWgt.arrayWeightsSum(srcArray);
       else
-        nmiss++;
+        numMissVals++;
     }
 
   progress::update(0, 1, 1);
 
-  field2.nmiss = nmiss;
+  field2.numMissVals = numMissVals;
 
   remap_grid_free(remap.srcGrid);
   remap_grid_free(remap.tgtGrid);
diff --git a/src/remap_grid_cell_search.cc b/src/remap_grid_cell_search.cc
index 9d6ba1ff2747f50c69ec0d43312bec2ae9cc2f27..48bd91e7acbad63f8dae7b06788982c0a5aa6c3a 100644
--- a/src/remap_grid_cell_search.cc
+++ b/src/remap_grid_cell_search.cc
@@ -5,7 +5,7 @@
 
 */
 
-//#define WITH_XYZCOORDS 1
+// #define WITH_XYZCOORDS 1
 
 #include "remap_grid_cell_search.h"
 #include "cdo_output.h"
diff --git a/src/remap_point_search.cc b/src/remap_point_search.cc
index 9e70c0dad6f9204f4a0618d79435e34176ab32c6..42365a4df48528969f6329b3d98efa7b249e8fe3 100644
--- a/src/remap_point_search.cc
+++ b/src/remap_point_search.cc
@@ -10,7 +10,7 @@
 #include "cdo_math.h"
 #include "remap.h"
 #include <mpim_grid.h>
-#include "cdo_output.h" 
+#include "cdo_output.h"
 
 static size_t
 fill_src_indices(bool isCyclic, long nx, long ny, long ii, long jj, long k, size_t *psrcIndices)
@@ -44,7 +44,8 @@ fill_src_indices(bool isCyclic, long nx, long ny, long ii, long jj, long k, size
 }
 
 static void
-store_distance_healpix(GridPointSearch &gps, double plon, double plat, knnWeightsType &knnWeights, size_t numIndices, size_t *indices, double *srcLons, double *srcLats)
+store_distance_healpix(GridPointSearch &gps, double plon, double plat, knnWeightsType &knnWeights, size_t numIndices,
+                       size_t *indices, double *srcLons, double *srcLats)
 {
   double xyz[3], query_pt[3];
   gcLLtoXYZ(plon, plat, query_pt);
@@ -137,9 +138,8 @@ gridSearchPointHealpix(GridPointSearch &gps, double plon, double plat, knnWeight
     if (neighbours[i] >= 0) indices[numWeights++] = neighbours[i];
 
   double srcLons[9], srcLats[9];
-  for (size_t i = 0; i < numWeights; ++i)
-    hp_index_to_lonlat(gps.order, gps.nside, indices[i], &srcLons[i], &srcLats[i]);
-    
+  for (size_t i = 0; i < numWeights; ++i) hp_index_to_lonlat(gps.order, gps.nside, indices[i], &srcLons[i], &srcLats[i]);
+
   store_distance_healpix(gps, plon, plat, knnWeights, numWeights, indices, srcLons, srcLats);
 }
 
@@ -210,7 +210,8 @@ gridSearchPointReg2d(GridPointSearch &gps, double plon, double plat, knnWeightsT
           size_t nbrIndices4[4];
           double nbrDistance4[4];
           for (size_t n = 0; n < numNeighbors; ++n) nbrIndices4[n] = SIZE_MAX;
-          searchResult = grid_search_square_reg_2d_NN(nx, ny, nbrIndices4, nbrDistance4, plat, plon, src_center_lat, src_center_lon);
+          searchResult
+              = grid_search_square_reg_2d_NN(nx, ny, nbrIndices4, nbrDistance4, plat, plon, src_center_lat, src_center_lon);
           if (searchResult < 0)
             {
               for (size_t n = 0; n < numNeighbors; ++n) nbrIndices[n] = nbrIndices4[n];
@@ -219,8 +220,8 @@ gridSearchPointReg2d(GridPointSearch &gps, double plon, double plat, knnWeightsT
         }
       else
         {
-          searchResult
-              = grid_search_square_reg_2d_NN(nx, ny, nbrIndices.data(), nbrDistance.data(), plat, plon, src_center_lat, src_center_lon);
+          searchResult = grid_search_square_reg_2d_NN(nx, ny, nbrIndices.data(), nbrDistance.data(), plat, plon, src_center_lat,
+                                                      src_center_lon);
         }
 
       if (searchResult >= 0)
@@ -408,5 +409,6 @@ remap_search_square(RemapSearch &rsearch, const LonLatPoint &llpoint, size_t (&s
   else if (rsearch.gps.in_use)
     return gridSearchSquareCurv2d(rsearch.gps, rsearch.srcGrid, srcIndices, srcLats, srcLons, llpoint.lat, llpoint.lon);
   else
-    return grid_search_square_curv_2d_scrip(rsearch.srcGrid, srcIndices, srcLats, srcLons, llpoint.lat, llpoint.lon, rsearch.srcBins);
+    return grid_search_square_curv_2d_scrip(rsearch.srcGrid, srcIndices, srcLats, srcLons, llpoint.lat, llpoint.lon,
+                                            rsearch.srcBins);
 }
diff --git a/src/remap_scrip_io.cc b/src/remap_scrip_io.cc
index 5a2d6d07cfa3379e85b40360699101bf20895442..4db2095ba183d687cdd2ca23f93d9a7b0e06e4ac 100644
--- a/src/remap_scrip_io.cc
+++ b/src/remap_scrip_io.cc
@@ -50,10 +50,7 @@ write_array_int64(int ncId, int ncVarId, nc_type xtype, size_t arraySize, size_t
   if (xtype == NC_NAT) {}
 #ifdef HAVE_NETCDF4
 #ifdef NC_UINT64
-  else if (xtype == NC_UINT64)
-    {
-      nce(nc_put_var_ulonglong(ncId, ncVarId, (unsigned long long *) array));
-    }
+  else if (xtype == NC_UINT64) { nce(nc_put_var_ulonglong(ncId, ncVarId, (unsigned long long *) array)); }
 #endif
 #endif
   else
@@ -73,10 +70,7 @@ read_array_int64(int ncId, int ncVarId, size_t arraySize, size_t *array)
   if (xtype == NC_NAT) {}
 #ifdef HAVE_NETCDF4
 #ifdef NC_UINT64
-  else if (xtype == NC_UINT64)
-    {
-      nce(nc_get_var_ulonglong(ncId, ncVarId, (unsigned long long *) array));
-    }
+  else if (xtype == NC_UINT64) { nce(nc_get_var_ulonglong(ncId, ncVarId, (unsigned long long *) array)); }
 #endif
 #endif
   else
@@ -270,7 +264,7 @@ remap_write_data_scrip(const std::string &weightsfile, const RemapSwitches &rema
   auto srcSizetype = (srcGrid.size > IndexLimit) ? largeSizetype : NC_INT;
   auto dstSizetype = (tgtGrid.size > IndexLimit) ? largeSizetype : NC_INT;
 
-  auto compress{Compress::NONE};
+  auto compress{ Compress::NONE };
 #ifdef HAVE_NETCDF4
   if (CdoDefault::FileType == CDI_FILETYPE_NC4 || CdoDefault::FileType == CDI_FILETYPE_NC4C)
     {
@@ -530,14 +524,8 @@ get_maptype(int ncId)
       int status = nc_get_att_int(ncId, NC_GLOBAL, "remap_order", &iatt);
       if (status == NC_NOERR) remapSwitches.remapOrder = iatt;
     }
-  else if (mapMethod.starts_with("Bilinear"))
-    {
-      remapSwitches.mapType = RemapMethod::BILINEAR;
-    }
-  else if (mapMethod.starts_with("Bicubic"))
-    {
-      remapSwitches.mapType = RemapMethod::BICUBIC;
-    }
+  else if (mapMethod.starts_with("Bilinear")) { remapSwitches.mapType = RemapMethod::BILINEAR; }
+  else if (mapMethod.starts_with("Bicubic")) { remapSwitches.mapType = RemapMethod::BICUBIC; }
   else if (mapMethod.starts_with("Distance"))
     {
       int numNeighbors = 4;
diff --git a/src/remap_search_latbins.cc b/src/remap_search_latbins.cc
index 9ffa4b42dfeb7e110c8759fdd018144badc46a07..af7cd615fbdf177482f231a807741b3bd2edceef 100644
--- a/src/remap_search_latbins.cc
+++ b/src/remap_search_latbins.cc
@@ -394,7 +394,7 @@ grid_search_square_curv_2d_scrip(RemapGrid *srcGrid, size_t (&srcIndices)[4], do
     printf("Using nearest-neighbor average for this point\n");
   */
   searchResult = gridSearchSquareCurv2dNNScrip(min_add, max_add, srcIndices, srcLats, plat, plon, srcGrid->cell_center_lat.data(),
-                                                srcGrid->cell_center_lon.data());
+                                               srcGrid->cell_center_lon.data());
 
   return searchResult;
 }
diff --git a/src/remap_search_reg2d.cc b/src/remap_search_reg2d.cc
index b5f6aa7322030731f1c83b889211689b7143fd1e..2f0856ff420d9a47798165125fa2b8669a6b7762 100644
--- a/src/remap_search_reg2d.cc
+++ b/src/remap_search_reg2d.cc
@@ -9,7 +9,7 @@
 
 static void
 nbr_indices_and_distance(int &searchResult, size_t jj, size_t ii, size_t nx, double &dist_min, double distance, size_t *nbrIndices,
-                 double *nbrDistance)
+                         double *nbrDistance)
 {
   if (distance < dist_min)
     {
diff --git a/src/remap_store_link.cc b/src/remap_store_link.cc
index 664ead2ee87dc8d5fd675a4bd7d39d2f5597a490..e859a60be376510f86bda226951ffafe2f44cb86 100644
--- a/src/remap_store_link.cc
+++ b/src/remap_store_link.cc
@@ -141,10 +141,7 @@ weight_links_to_remap_links(int doAlloc, size_t gridSize, std::vector<WeightLink
               if (numLinks) delete[] weightLinks[i].indexWeights;
             }
         }
-      else
-        {
-          delete[] weightLinks[0].indexWeights;
-        }
+      else { delete[] weightLinks[0].indexWeights; }
     }
 }
 
diff --git a/src/remap_store_link.h b/src/remap_store_link.h
index 2821f1e8dc2f24b26250f1fe3ae821e29065ad26..6fee3a82d60ba2c65afc8f392efc1c648bfe7487 100644
--- a/src/remap_store_link.h
+++ b/src/remap_store_link.h
@@ -44,7 +44,8 @@ void weight_links_alloc(size_t numNeighbors, size_t gridSize, std::vector<Weight
 void weight_links_4_alloc(size_t gridSize, std::vector<WeightLinks4> &weightLinks);
 void store_weightlinks(int doAlloc, size_t numWeights, const size_t *indices, const double *weights, size_t cellIndex,
                        std::vector<WeightLinks> &weightLinks);
-void store_weightlinks_bicubic(const size_t *indices, double (&weights)[4][4], size_t cellIndex, std::vector<WeightLinks4> &weightLinks);
+void store_weightlinks_bicubic(const size_t *indices, double (&weights)[4][4], size_t cellIndex,
+                               std::vector<WeightLinks4> &weightLinks);
 void weight_links_to_remap_links(int doAlloc, size_t gridSize, std::vector<WeightLinks> &weightLinks, RemapVars &rv);
 void weight_links_4_to_remap_links(size_t gridSize, std::vector<WeightLinks4> &weightLinks, RemapVars &rv);
 
diff --git a/src/remap_store_link_cnsrv.cc b/src/remap_store_link_cnsrv.cc
index 82263f1804302579a6890d088f4bfa0da760a55f..59a6721dbd6e55fada4a08e48bf2d917809921e3 100644
--- a/src/remap_store_link_cnsrv.cc
+++ b/src/remap_store_link_cnsrv.cc
@@ -5,7 +5,8 @@
 
 */
 
-#include "dmemory.h"
+#include <stdlib.h> // malloc()
+
 #include "process_int.h"
 #include "cdo_options.h"
 #include "remap.h"
@@ -35,9 +36,9 @@ grid_store_init(GridStore &grid_store, long gridsize)
     fprintf(stdout, "blksize = %ld  lastblksize = %ld  max_size = %ld  nblocks = %ld\n", grid_store.blk_size,
             grid_store.max_size % grid_store.blk_size, grid_store.max_size, grid_store.nblocks);
 
-  grid_store.blksize = (long *) Malloc(grid_store.nblocks * sizeof(long));
-  grid_store.nlayers = (long *) Malloc(grid_store.nblocks * sizeof(long));
-  grid_store.layers = (GridLayer **) Malloc(grid_store.nblocks * sizeof(GridLayer *));
+  grid_store.blksize = (long *) malloc(grid_store.nblocks * sizeof(long));
+  grid_store.nlayers = (long *) malloc(grid_store.nblocks * sizeof(long));
+  grid_store.layers = (GridLayer **) malloc(grid_store.nblocks * sizeof(GridLayer *));
 
   nblocks = grid_store.nblocks;
   for (iblk = 0; iblk < nblocks; ++iblk)
@@ -71,9 +72,9 @@ grid_store_delete(GridStore &grid_store)
             }
           */
           GridLayer *grid_layer_f = grid_layer;
-          Free(grid_layer->grid2_link);
+          free(grid_layer->grid2_link);
           grid_layer = grid_layer->next;
-          Free(grid_layer_f);
+          free(grid_layer_f);
         }
       /*
       if ( Options::cdoVerbose )
@@ -84,9 +85,9 @@ grid_store_delete(GridStore &grid_store)
       */
     }
 
-  Free(grid_store.blksize);
-  Free(grid_store.layers);
-  Free(grid_store.nlayers);
+  free(grid_store.blksize);
+  free(grid_store.layers);
+  free(grid_store.nlayers);
 }
 
 /*
@@ -153,9 +154,9 @@ store_link_cnsrv(RemapVars &rv, long add1, long add2, long numWeights, double *w
   if (ilayer < grid_store.nlayers[iblk]) { grid_layer->grid2_link[iadd2] = nlink; }
   else
     {
-      grid_layer = (GridLayer *) Malloc(sizeof(GridLayer));
+      grid_layer = (GridLayer *) malloc(sizeof(GridLayer));
       grid_layer->next = nullptr;
-      grid_layer->grid2_link = (long *) Malloc(grid_store.blksize[iblk] * sizeof(long));
+      grid_layer->grid2_link = (long *) malloc(grid_store.blksize[iblk] * sizeof(long));
 
       long blksize = grid_store.blksize[iblk];
       for (long i = 0; i < blksize; ++i) grid_layer->grid2_link[i] = -1;
diff --git a/src/remap_utils.cc b/src/remap_utils.cc
index 617806ca3b81a325f108fde5802c14ee9aeaca09..186f426a87828b50a15b12ed7f9cf6d155ccaddf 100644
--- a/src/remap_utils.cc
+++ b/src/remap_utils.cc
@@ -14,7 +14,8 @@
 void remap_sort_addr(RemapVars &rv);
 
 void
-remap_print_info(int operfunc, bool remap_genweights, const RemapGrid &srcGrid, const RemapGrid &tgtGrid, size_t nmiss, int numNeighbors)
+remap_print_info(int operfunc, bool remap_genweights, const RemapGrid &srcGrid, const RemapGrid &tgtGrid, size_t numMissVals,
+                 int numNeighbors)
 {
   std::string outStr;
   // clang-format off
@@ -43,13 +44,13 @@ remap_print_info(int operfunc, bool remap_genweights, const RemapGrid &srcGrid,
   if (tgtGrid.rank == 2) outStr += "x" + std::to_string(tgtGrid.dims[1]);
   outStr += ") grid";
 
-  if (nmiss) outStr += ", with source mask (" + std::to_string(gridInqSize(srcGrid.gridID) - nmiss) + ")";
+  if (numMissVals) outStr += ", with source mask (" + std::to_string(gridInqSize(srcGrid.gridID) - numMissVals) + ")";
 
   cdo_print(outStr);
 }
 
 void
-remap_print_warning(const std::string &remapWeightsFile, int operfunc, const RemapGrid &srcGrid, size_t nmiss)
+remap_print_warning(const std::string &remapWeightsFile, int operfunc, const RemapGrid &srcGrid, size_t numMissVals)
 {
   (void) operfunc;
 
@@ -61,7 +62,7 @@ remap_print_warning(const std::string &remapWeightsFile, int operfunc, const Rem
   if (srcGrid.rank == 2) outStr += "x" + std::to_string(srcGrid.dims[1]);
   outStr += ") grid";
 
-  if (nmiss) outStr += " with mask (" + std::to_string(gridInqSize(srcGrid.gridID) - nmiss) + ")";
+  if (numMissVals) outStr += " with mask (" + std::to_string(gridInqSize(srcGrid.gridID) - numMissVals) + ")";
 
   outStr += " not found!";
 
@@ -410,5 +411,5 @@ remap_init(RemapType &remap)
   remap.nused = 0;
   remap.gridID = -1;
   remap.gridsize = 0;
-  remap.nmiss = 0;
+  remap.numMissVals = 0;
 }
diff --git a/src/remap_utils.h b/src/remap_utils.h
index d49bcd1f00d6117271b534402b200b36fb4a1478..24ad644663adc4073b9db919d077f6cb04128fc6 100644
--- a/src/remap_utils.h
+++ b/src/remap_utils.h
@@ -58,8 +58,9 @@ remap_func_is_dist(int operfunc)
   return (operfunc == REMAPDIS || operfunc == GENDIS || operfunc == REMAPNN || operfunc == GENNN);
 }
 
-void remap_print_info(int operfunc, bool remap_genweights, const RemapGrid &srcGrid, const RemapGrid &tgtGrid, size_t nmiss, int numNeighbors);
-void remap_print_warning(const std::string &remapWeightsFile, int operfunc, const RemapGrid &srcGrid, size_t nmiss);
+void remap_print_info(int operfunc, bool remap_genweights, const RemapGrid &srcGrid, const RemapGrid &tgtGrid, size_t numMissVals,
+                      int numNeighbors);
+void remap_print_warning(const std::string &remapWeightsFile, int operfunc, const RemapGrid &srcGrid, size_t numMissVals);
 
 RemapParams remap_get_params();
 void remap_set_params(const RemapParams &remapParams);
diff --git a/src/remap_vars.cc b/src/remap_vars.cc
index aa0cd6096a2da70ee366f4273eaf70660f56203a..52feb2a2acac9075b0f7b71c17cd124d30d33bc7 100644
--- a/src/remap_vars.cc
+++ b/src/remap_vars.cc
@@ -178,8 +178,8 @@ remap(Varray<T2> &tgtArray, T2 missval, size_t tgtSize, const RemapVars &rv, con
       tgtIndices    target indices for each link
       srcIndices    source indices for each link
       numWeights    num of weights used in remapping
-      weights    remapping weights for each link
-      srcArray  array with source field to be remapped
+      weights       remapping weights for each link
+      srcArray      array with source field to be remapped
 
     Optional:
 
@@ -189,7 +189,6 @@ remap(Varray<T2> &tgtArray, T2 missval, size_t tgtSize, const RemapVars &rv, con
 
       tgtArray  array for remapped field on target grid
   */
-  // if (Options::cdoVerbose) cdo_print("Remap links is sorted: %s", is_sorted_list() ? "true" : "false");
   if (Options::cdoVerbose) cdo_print("Remap links per value: %ld", rv.linksPerValue);
 
   cdo::timer timer;
@@ -288,7 +287,7 @@ remap_laf(Varray<T2> &tgtArray, T2 missval, size_t tgtSize, const RemapVars &rv,
   const auto &tgtIndices = rv.tgtCellIndices;  // target indices for each link
   const auto &srcIndices = rv.srcCellIndices;  // source indices for each link
 
-  varray_fill(tgtSize, tgtArray, missval);
+  ranges::fill(tgtArray, missval);
 
   if (numLinks == 0) return;
 
@@ -316,8 +315,8 @@ remap_laf(Varray<T2> &tgtArray, T2 missval, size_t tgtSize, const RemapVars &rv,
       auto &src_cls = src_cls2[ompthID];
       auto &src_weights = src_weights2[ompthID];
 #endif
-      varray_fill(src_cls, static_cast<T1>(0.0));
-      varray_fill(src_weights, 0.0);
+      ranges::fill(src_cls, static_cast<T1>(0.0));
+      ranges::fill(src_weights, 0.0);
 
       // only for sorted tgtIndices!
       {
diff --git a/src/remaplib.cc b/src/remaplib.cc
index acbe5fdb8b0f899f565c56b429c1ca3b8e4659ea..c5753074aa6b393f07ea44957b618c0210c7dcaa 100644
--- a/src/remaplib.cc
+++ b/src/remaplib.cc
@@ -766,15 +766,15 @@ remap_init_grids(RemapMethod mapType, bool doExtrapolate, int gridID1, RemapGrid
         srcGrid.type = RemapGridType::HealPix;
     }
 
-  //#ifdef YAC_CELL_SEARCH
-  // if (is_reg2d_grid(gridID2) && mapType == RemapMethod::CONSERV) tgtGrid.type = RemapGridType::Reg2D;
-  //#else
+  // #ifdef YAC_CELL_SEARCH
+  //  if (is_reg2d_grid(gridID2) && mapType == RemapMethod::CONSERV) tgtGrid.type = RemapGridType::Reg2D;
+  // #else
   if (srcGrid.type == RemapGridType::Reg2D)
     {
       if (is_reg2d_grid(gridID2) && mapType == RemapMethod::CONSERV) tgtGrid.type = RemapGridType::Reg2D;
       // else srcGrid.type = -1;
     }
-  //#endif
+  // #endif
 
   if (!remap_gen_weights && is_reg2d_grid(gridID2) && tgtGrid.type != RemapGridType::Reg2D)
     {
@@ -885,7 +885,7 @@ remap_stat(int remapOrder, RemapGrid &srcGrid, RemapGrid &tgtGrid, RemapVars &rv
   std::vector<size_t> tgt_count(tgtGrid.size, 0);
 
 #ifdef HAVE_OPENMP4
-  //#pragma omp simd -> wrong result with clang
+  // #pragma omp simd -> wrong result with clang
 #endif
   for (size_t i = 0; i < rv.numLinks; ++i) tgt_count[rv.tgtCellIndices[i]]++;
 
diff --git a/src/remapsort.cc b/src/remapsort.cc
index 3b7650d816df4971496eda4b1a1f3629530e5436..09374cc43b2208e5a328cf892773735583e5335b 100644
--- a/src/remapsort.cc
+++ b/src/remapsort.cc
@@ -219,7 +219,7 @@ sort_add(size_t numLinks, size_t numWeights, size_t *add1, size_t *add2, double
 */
 
 /* MERGE SORT DEFINES */
-//#define MERGE_SORT_CHUNKS          64
+// #define MERGE_SORT_CHUNKS          64
 #define MERGE_SORT_LIMIT_SIZE 4096  // numLinks/(MERGE_SORT_CHUNKS*omp_get_num_procs())
 
 static void
diff --git a/src/selboxinfo.h b/src/selboxinfo.h
index 2d417f648d33087b192763b84fff29ad4d494862..629b17c5626998350518f61e4cb9610c281c7783 100644
--- a/src/selboxinfo.h
+++ b/src/selboxinfo.h
@@ -1,6 +1,8 @@
 #ifndef SELBOXINFO_H
 #define SELBOXINFO_H
 
+#include <vector>
+
 struct SelboxInfo
 {
   std::vector<long> cellidx;
diff --git a/src/sellist.cc b/src/sellist.cc
index 5c7c844535260ff0d112b4de08aa9824d8b9df7b..e9f63b7d1dec4178741ef3c6d44559f3a09a723d 100644
--- a/src/sellist.cc
+++ b/src/sellist.cc
@@ -241,6 +241,36 @@ selinfo_check(SelectInfo &selInfo, int listIdx, void *par)
   return found;
 }
 
+bool
+selinfo_check_index(SelectInfo &selInfo, int listIdx, int ival, int maxValues)
+{
+  auto &selList = selInfo.selList;
+  auto found = false;
+
+  if (!selInfo.isValidListIdx(listIdx)) return found;
+
+  auto nvalues = selInfo.nvalues(listIdx);
+  if (nvalues)
+    {
+      auto &e = selList[listIdx];
+      if (e.type == SelType::INT)
+        {
+          for (int i = 0; i < nvalues; ++i)
+            {
+              int eval = e.ivalues[i];
+              if (eval < 0) eval = maxValues + eval + 1;
+              if (ival == eval)
+                {
+                  found = true;
+                  e.flag[i] = true;
+                }
+            }
+        }
+    }
+
+  return found;
+}
+
 bool
 selinfo_check_date(SelectInfo &selInfo, int listIdx, const char *par)
 {
diff --git a/src/sellist.h b/src/sellist.h
index 8733d74f59652a77929e7037ddf69915c515b07f..7692b841b2a61e31e82821edf6db37d941299ed2 100644
--- a/src/sellist.h
+++ b/src/sellist.h
@@ -63,6 +63,7 @@ public:
 #define SELINFO_CHECK_FLAG(name) selinfo_check_flag(selInfo, listIdx_##name)
 #define SELINFO_CHECK_RANGE_FLAG(name) selinfo_check_range_flag(selInfo, listIdx_##name)
 #define SELINFO_CHECK(name) selinfo_check(selInfo, listIdx_##name, &name)
+#define SELINFO_CHECK_INDEX(name, numValues) selinfo_check_index(selInfo, listIdx_##name, name, numValues)
 #define SELINFO_CHECK_DATE(name) selinfo_check_date(selInfo, listIdx_##name, name)
 #define SELINFO_CHECK_SEASON(name, month) selinfo_check_season(selInfo, listIdx_##name, month)
 #define SELINFO_CHECK_RANGE(name, value) selinfo_check_range(selInfo, listIdx_##name, value)
@@ -74,6 +75,7 @@ int selinfo_add(SelectInfo &selInfo, const std::string &description, const std::
 void selinfo_check_flag(const SelectInfo &selInfo, int listIdx);
 void selinfo_check_range_flag(const SelectInfo &selInfo, int listIdx);
 bool selinfo_check(SelectInfo &selInfo, int listIdx, void *par);
+bool selinfo_check_index(SelectInfo &selInfo, int listIdx, int par, int maxValues);
 bool selinfo_check_date(SelectInfo &selInfo, int listIdx, const char *par);
 bool selinfo_check_season(SelectInfo &selInfo, int listIdx, int month);
 bool selinfo_check_range(SelectInfo &selInfo, int listIdx, double value);
diff --git a/src/specspace.h b/src/specspace.h
index 800c16ecd97465c33e3a8ec7c188df68c9367efa..87162d0b351411bcbbe8c52b13dce8e4fe10e2e5 100644
--- a/src/specspace.h
+++ b/src/specspace.h
@@ -12,6 +12,7 @@
 #endif
 
 #include "cdo_options.h"
+#include "cdo_output.h"
 #include "transform.h"
 #include "varray.h"
 
@@ -72,7 +73,7 @@ public:
     if (use_fftw == false)
       {
         vtrig.resize(nlon);
-        const auto status = fft_set(vtrig.data(), ifax, nlon);
+        auto status = fft_set(vtrig.data(), ifax, nlon);
         if (status < 0) cdo_abort("FFT error!");
       }
   }
diff --git a/src/statistic.cc b/src/statistic.cc
index 1106ba15e10ecea7bc0e834288e098eb887b8cb9..51c00d752d90a6d54c53e91bdb007e70cf77b992 100644
--- a/src/statistic.cc
+++ b/src/statistic.cc
@@ -40,7 +40,7 @@ gamma_help_1(double a, double x)
   double ap = a;
   double sum, del = sum = 1.0 / a;
 
-  for (int i = 1; i <= 100; ++i) // 100 iterations
+  for (int i = 1; i <= 100; ++i)  // 100 iterations
     {
       ap++;
       del *= x / ap;
@@ -66,7 +66,7 @@ gamma_help_2(double a, double x)
   double d = 1.0 / b;
   double h = d;
 
-  for (int i = 1; i <= 100; ++i) // 100 iterations
+  for (int i = 1; i <= 100; ++i)  // 100 iterations
     {
       double an = -i * (i - a);
       b += 2.0;
diff --git a/src/table.cc b/src/table.cc
index 3ee6a416c9d3b2bc1f254e5c8de993b7cd8ebb5e..b5fd1fbf2c8362461f8300304fa882de6530b987 100644
--- a/src/table.cc
+++ b/src/table.cc
@@ -40,4 +40,4 @@ define_table(const std::string &tablearg)
   return tableID;
 }
 
-}
+}  // namespace cdo
diff --git a/src/util_date.h b/src/util_date.h
index 72e552e8ffcb4b0b5720a0e884a28c3af9c2de13..5949763b248fd3825c224f73999fe4e4667fbfee 100644
--- a/src/util_date.h
+++ b/src/util_date.h
@@ -2,6 +2,9 @@
 #define UTIL_DATE_H
 
 #include <cstring>
+#include <cstdio>
+
+#include "cdi_datetime.h"
 
 enum
 {
diff --git a/src/util_files.cc b/src/util_files.cc
index 4327362e65231142470a4284e81f0a8d8f8d0d72..4790156226166facb864a24f85c18f64d7e99b7a 100644
--- a/src/util_files.cc
+++ b/src/util_files.cc
@@ -8,17 +8,34 @@
 
 #include "util_files.h"
 #include "cdo_options.h"
+// #include "cdo_output.h"
 #include "cdo_vlist.h"
-#include "readline.h"
 
 #include "cdo_default_values.h"
 
 bool
 FileUtils::file_exists(const std::string &fileName)
 {
-  struct stat buf;
-  auto status = stat(fileName.c_str(), &buf);
-  return (status == 0) && (S_ISREG(buf.st_mode) && buf.st_size > 0);
+  /*
+  auto isZarr = (fileName.starts_with("file://") && fileName.find("zarr", 6) != std::string::npos);
+  if (isZarr)
+    {
+      cdo_abort("Enlargement of %s not possible!", fileName);
+      int start = 7;
+      auto pos = fileName.find("#mode", start);
+      if (pos == std::string::npos) return false;
+      auto zarrName = fileName.substr(start, pos - start);
+      struct stat buf;
+      auto status = stat(zarrName.c_str(), &buf);
+      return (status == 0) && (S_ISDIR(buf.st_mode) && buf.st_size > 0);
+    }
+  else
+  */
+    {
+      struct stat buf;
+      auto status = stat(fileName.c_str(), &buf);
+      return (status == 0) && (S_ISREG(buf.st_mode) && buf.st_size > 0);
+    }
 }
 
 bool
@@ -29,18 +46,16 @@ FileUtils::user_file_overwrite(const std::string &fileName)
   if (!Options::silentMode && cdo::stdinIsTerminal && cdo::stderrIsTerminal)
     {
       fprintf(stderr, "File %s already exists, overwrite? (yes/no): ", fileName.c_str());
-      char line[1024];
-      cdo::readline(stdin, line, 1024);
-      char *pline = line;
-      while (isspace((int) *pline)) pline++;
-      auto len = strlen(pline);
-      if (len == 3)
+      std::string line;
+      std::getline(std::cin, line);
+      while (std::isspace((int) line[0])) line.erase(0, 1);
+      if (line.size() == 3)
         {
-          if (std::strncmp(pline, "yes", 3) == 0 || std::strncmp(pline, "YES", 3) == 0) status = true;
+          if (line.starts_with("yes") || line.starts_with("YES")) status = true;
         }
-      else if (len == 1)
+      else if (line.size() == 1)
         {
-          if (pline[0] == 'y' || pline[0] == 'Y') status = true;
+          if (line[0] == 'y' || line[0] == 'Y') status = true;
         }
     }
 
diff --git a/src/util_string.cc b/src/util_string.cc
index 4adcac9c2767dd5b62feff8130cedaf5688e769c..1757748ccf42355e7b33d3e5c180a01a8e727af7 100644
--- a/src/util_string.cc
+++ b/src/util_string.cc
@@ -36,14 +36,14 @@ split_string(const std::string &str, const std::string &delimiter)
 std::string
 string_to_upper(std::string str)
 {
-  std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::toupper(c); });
+  std::ranges::transform(str, str.begin(), ::toupper);
   return str;
 }
 
 std::string
 string_to_lower(std::string str)
 {
-  std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
+  std::ranges::transform(str, str.begin(), ::tolower);
   return str;
 }
 
@@ -142,7 +142,7 @@ split_with_seperator(const std::string &sourceString, const char seperator)
 bool
 string_is_float(const std::string &str)
 {
-  if (str.empty() || isspace(str[0])) return 0;
+  if (str.empty() || std::isspace(str[0])) return 0;
 
   char *ptr = nullptr;
   strtod(str.c_str(), &ptr);
@@ -152,7 +152,7 @@ string_is_float(const std::string &str)
 bool
 string_is_int(const std::string &str)
 {
-  if (str.empty() || isspace(str[0])) return 0;
+  if (str.empty() || std::isspace(str[0])) return 0;
 
   char *ptr = nullptr;
   strtol(str.c_str(), &ptr, 10);
@@ -171,7 +171,7 @@ tokenize_comma_seperated_int_list(const std::string &args)
 {
   auto tokens = split_with_seperator(args, ',');
   bool res = true;
-  for (auto t : tokens)
+  for (const auto &t : tokens)
     {
       if (!string_is_int(t))
         {
diff --git a/src/util_string.h b/src/util_string.h
index b5fbb3b179de1394719192ba18e2bd0be1e09f52..5ad3ebafe62be7a3b5050f0e69c8918d435c0058 100644
--- a/src/util_string.h
+++ b/src/util_string.h
@@ -13,6 +13,7 @@
 #include <vector>
 #include <memory>
 #include <stdexcept>
+#include <algorithm>
 
 #define ADD_PLURAL(n) ((n) != 1 ? "s" : "")
 
@@ -51,4 +52,32 @@ string_format(const std::string &format, Args... args)
   return std::string(buf.get(), buf.get() + size - 1);  // We don't want the '\0' inside
 }
 
+namespace Util
+{
+namespace String
+{
+static inline std::string
+ltrim(std::string s)
+{
+  s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
+  return s;
+}
+
+static inline std::string
+rtrim(std::string s)
+{
+  s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
+  return s;
+}
+
+static inline std::string
+trim(std::string s)
+{
+  s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
+  s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
+  return s;
+}
+}  // namespace String
+}  // namespace Util
+
 #endif
diff --git a/src/varray.cc b/src/varray.cc
index 3d718fb831768f877dcf2a72e37c83c84c94627a..651d3e722c5e54f4fa321bef325356c4064cf649 100644
--- a/src/varray.cc
+++ b/src/varray.cc
@@ -15,7 +15,6 @@
 #include "cimdOmp.h"
 #include "arithmetic.h"
 
-
 template <typename T>
 inline T
 min_value(T v1, T v2)
@@ -72,7 +71,7 @@ template MinMax varray_min_max_mv(size_t len, const Varray<double> &v, double mi
 
 template <typename T>
 MinMaxSum
-varray_min_max_sum(size_t len, const Varray<T> &v, MinMaxSum mms)
+varray_min_max_sum(size_t len, const Varray<T> &v, const MinMaxSum &mms)
 {
   auto f_minmaxsum = [](auto val, auto &vmin, auto &vmax, auto &vsum) {
     vmin = min_value(vmin, val);
@@ -107,12 +106,12 @@ varray_min_max_sum(size_t len, const Varray<T> &v, MinMaxSum mms)
 }
 
 // Explicit instantiation
-template MinMaxSum varray_min_max_sum(size_t len, const Varray<float> &v, MinMaxSum mms);
-template MinMaxSum varray_min_max_sum(size_t len, const Varray<double> &v, MinMaxSum mms);
+template MinMaxSum varray_min_max_sum(size_t len, const Varray<float> &v, const MinMaxSum &mms);
+template MinMaxSum varray_min_max_sum(size_t len, const Varray<double> &v, const MinMaxSum &mms);
 
 template <typename T>
 MinMaxSum
-varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, MinMaxSum mms)
+varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, const MinMaxSum &mms)
 {
   auto f_minmaxsum_mv = [](auto val, auto mv, auto &vmin, auto &vmax, auto &vsum, auto &nvals, auto is_EQ) {
     if (!is_EQ(val, mv))
@@ -133,7 +132,8 @@ varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, MinMaxSum mms)
     {
 #ifndef __ICC  // wrong result with icc19
 #ifdef HAVE_OPENMP4
-#pragma omp parallel for simd if (len > cdoMinLoopSize) default(shared) schedule(static) reduction(min : vmin) reduction(max : vmax) reduction(+ : vsum,nvals)
+#pragma omp parallel for simd if (len > cdoMinLoopSize) default(shared) schedule(static) reduction(min : vmin) \
+    reduction(max : vmax) reduction(+ : vsum, nvals)
 #endif
 #endif
       for (size_t i = 0; i < len; ++i) f_minmaxsum_mv((double) v[i], missval, vmin, vmax, vsum, nvals, dbl_is_equal);
@@ -142,7 +142,8 @@ varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, MinMaxSum mms)
     {
 #ifndef __ICC  // wrong result with icc19
 #ifdef HAVE_OPENMP4
-#pragma omp parallel for simd if (len > cdoMinLoopSize) default(shared) schedule(static) reduction(min : vmin) reduction(max : vmax) reduction(+ : vsum,nvals)
+#pragma omp parallel for simd if (len > cdoMinLoopSize) default(shared) schedule(static) reduction(min : vmin) \
+    reduction(max : vmax) reduction(+ : vsum, nvals)
 #endif
 #endif
       for (size_t i = 0; i < len; ++i) f_minmaxsum_mv((double) v[i], missval, vmin, vmax, vsum, nvals, is_equal);
@@ -155,8 +156,8 @@ varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, MinMaxSum mms)
 }
 
 // Explicit instantiation
-template MinMaxSum varray_min_max_sum_mv(size_t len, const Varray<float> &v, float missval, MinMaxSum mms);
-template MinMaxSum varray_min_max_sum_mv(size_t len, const Varray<double> &v, double missval, MinMaxSum mms);
+template MinMaxSum varray_min_max_sum_mv(size_t len, const Varray<float> &v, float missval, const MinMaxSum &mms);
+template MinMaxSum varray_min_max_sum_mv(size_t len, const Varray<double> &v, double missval, const MinMaxSum &mms);
 
 template <typename T>
 MinMaxMean
@@ -250,18 +251,18 @@ template <typename T>
 size_t
 array_num_mv(size_t len, const T *array, T missval)
 {
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
 
   if (std::isnan(missval))
     {
-      for (size_t i = 0; i < len; ++i) count_mv(array[i], missval, nmiss, dbl_is_equal);
+      for (size_t i = 0; i < len; ++i) count_mv(array[i], missval, numMissVals, dbl_is_equal);
     }
   else
     {
-      for (size_t i = 0; i < len; ++i) count_mv(array[i], missval, nmiss, is_equal);
+      for (size_t i = 0; i < len; ++i) count_mv(array[i], missval, numMissVals, is_equal);
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 // Explicit instantiation
@@ -276,18 +277,18 @@ varray_num_mv(size_t len, const Varray<T> &v, T missval)
   assert(v.size() > 0);
   assert(len <= v.size());
 
-  size_t nmiss = 0;
+  size_t numMissVals = 0;
 
   if (std::isnan(missval))
     {
-      for (size_t i = 0; i < len; ++i) count_mv(v[i], missval, nmiss, dbl_is_equal);
+      for (size_t i = 0; i < len; ++i) count_mv(v[i], missval, numMissVals, dbl_is_equal);
     }
   else
     {
-      for (size_t i = 0; i < len; ++i) count_mv(v[i], missval, nmiss, is_equal);
+      for (size_t i = 0; i < len; ++i) count_mv(v[i], missval, numMissVals, is_equal);
     }
 
-  return nmiss;
+  return numMissVals;
 }
 
 // Explicit instantiation
@@ -411,7 +412,6 @@ varray_max(size_t len, const Varray<T> &v)
       for (size_t i = 0; i < len; ++i) vmax = max_value(vmax, v[i]);
     }
 
-
   return vmax;
 }
 
@@ -439,7 +439,7 @@ varray_range(size_t len, const Varray<T> &v)
           vmin = min_value(vmin, v[i]);
           vmax = max_value(vmax, v[i]);
         }
-      }
+    }
   else
     {
 #ifdef HAVE_OPENMP4
@@ -1099,10 +1099,10 @@ varray_prevarsum_mv(size_t len, const Varray<T> &v, T missval, double &rsum, dou
 
 template <typename T>
 double
-varray_var(size_t len, const Varray<T> &v, size_t nmiss, T missval)
+varray_var(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
 {
   double rsum = 0.0, rsumw = 0.0, rsumq = 0.0, rsumwq = 0.0;
-  if (nmiss > 0)
+  if (numMissVals > 0)
     varray_prevarsum_mv(len, v, missval, rsum, rsumw, rsumq, rsumwq);
   else
     varray_prevarsum(len, v, rsum, rsumw, rsumq, rsumwq);
@@ -1114,15 +1114,15 @@ varray_var(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 }
 
 // Explicit instantiation
-template double varray_var(size_t len, const Varray<float> &v, size_t nmiss, float missval);
-template double varray_var(size_t len, const Varray<double> &v, size_t nmiss, double missval);
+template double varray_var(size_t len, const Varray<float> &v, size_t numMissVals, float missval);
+template double varray_var(size_t len, const Varray<double> &v, size_t numMissVals, double missval);
 
 template <typename T>
 double
-varray_var_1(size_t len, const Varray<T> &v, size_t nmiss, T missval)
+varray_var_1(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
 {
   double rsum = 0.0, rsumw = 0.0, rsumq = 0.0, rsumwq = 0.0;
-  if (nmiss > 0)
+  if (numMissVals > 0)
     varray_prevarsum_mv(len, v, missval, rsum, rsumw, rsumq, rsumwq);
   else
     varray_prevarsum(len, v, rsum, rsumw, rsumq, rsumwq);
@@ -1134,8 +1134,8 @@ varray_var_1(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 }
 
 // Explicit instantiation
-template double varray_var_1(size_t len, const Varray<float> &v, size_t nmiss, float missval);
-template double varray_var_1(size_t len, const Varray<double> &v, size_t nmiss, double missval);
+template double varray_var_1(size_t len, const Varray<float> &v, size_t numMissVals, float missval);
+template double varray_var_1(size_t len, const Varray<double> &v, size_t numMissVals, double missval);
 
 template <typename T>
 static void
@@ -1210,10 +1210,10 @@ varray_weighted_prevarsum_mv(size_t len, const Varray<T> &v, const Varray<double
 
 template <typename T>
 double
-varray_weighted_var(size_t len, const Varray<T> &v, const Varray<double> &w, size_t nmiss, T missval)
+varray_weighted_var(size_t len, const Varray<T> &v, const Varray<double> &w, size_t numMissVals, T missval)
 {
   double rsum = 0.0, rsumw = 0.0, rsumq = 0.0, rsumwq = 0.0;
-  if (nmiss > 0)
+  if (numMissVals > 0)
     varray_weighted_prevarsum_mv(len, v, w, missval, rsum, rsumw, rsumq, rsumwq);
   else
     varray_weighted_prevarsum(len, v, w, rsum, rsumw, rsumq, rsumwq);
@@ -1225,15 +1225,15 @@ varray_weighted_var(size_t len, const Varray<T> &v, const Varray<double> &w, siz
 }
 
 // Explicit instantiation
-template double varray_weighted_var(size_t len, const Varray<float> &v, const Varray<double> &w, size_t nmiss, float missval);
-template double varray_weighted_var(size_t len, const Varray<double> &v, const Varray<double> &w, size_t nmiss, double missval);
+template double varray_weighted_var(size_t len, const Varray<float> &v, const Varray<double> &w, size_t numMissVals, float missval);
+template double varray_weighted_var(size_t len, const Varray<double> &v, const Varray<double> &w, size_t numMissVals, double missval);
 
 template <typename T>
 double
-varray_weighted_var_1(size_t len, const Varray<T> &v, const Varray<double> &w, size_t nmiss, T missval)
+varray_weighted_var_1(size_t len, const Varray<T> &v, const Varray<double> &w, size_t numMissVals, T missval)
 {
   double rsum = 0.0, rsumw = 0.0, rsumq = 0.0, rsumwq = 0.0;
-  if (nmiss > 0)
+  if (numMissVals > 0)
     varray_weighted_prevarsum_mv(len, v, w, missval, rsum, rsumw, rsumq, rsumwq);
   else
     varray_weighted_prevarsum(len, v, w, rsum, rsumw, rsumq, rsumwq);
@@ -1245,8 +1245,8 @@ varray_weighted_var_1(size_t len, const Varray<T> &v, const Varray<double> &w, s
 }
 
 // Explicit instantiation
-template double varray_weighted_var_1(size_t len, const Varray<float> &v, const Varray<double> &w, size_t nmiss, float missval);
-template double varray_weighted_var_1(size_t len, const Varray<double> &v, const Varray<double> &w, size_t nmiss, double missval);
+template double varray_weighted_var_1(size_t len, const Varray<float> &v, const Varray<double> &w, size_t numMissVals, float missval);
+template double varray_weighted_var_1(size_t len, const Varray<double> &v, const Varray<double> &w, size_t numMissVals, double missval);
 
 template <typename T>
 static void
@@ -1319,13 +1319,13 @@ varray_prekurtsum_mv(size_t len, const Varray<T> &v, T missval, double mean, dou
 
 template <typename T>
 double
-varray_kurt(size_t len, const Varray<T> &v, size_t nmiss, T missval)
+varray_kurt(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
 {
   double rsum3w;  // 3rd moment variables
   double rsum2diff, rsum4diff;
   double rsum, rsumw;
 
-  if (nmiss > 0)
+  if (numMissVals > 0)
     {
       varray_prevarsum0_mv(len, v, missval, rsum, rsumw);
       varray_prekurtsum_mv(len, v, missval, (rsum / rsumw), rsum3w, rsum2diff, rsum4diff);
@@ -1345,8 +1345,8 @@ varray_kurt(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 }
 
 // Explicit instantiation
-template double varray_kurt(size_t len, const Varray<float> &v, size_t nmiss, float missval);
-template double varray_kurt(size_t len, const Varray<double> &v, size_t nmiss, double missval);
+template double varray_kurt(size_t len, const Varray<float> &v, size_t numMissVals, float missval);
+template double varray_kurt(size_t len, const Varray<double> &v, size_t numMissVals, double missval);
 
 template <typename T>
 static void
@@ -1420,13 +1420,13 @@ varray_preskewsum_mv(size_t len, const Varray<T> &v, T missval, double mean, dou
 
 template <typename T>
 double
-varray_skew(size_t len, const Varray<T> &v, size_t nmiss, T missval)
+varray_skew(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
 {
   double rsum3w;  // 3rd moment variables
   double rsum3diff, rsum2diff;
   double rsum, rsumw;
 
-  if (nmiss > 0)
+  if (numMissVals > 0)
     {
       varray_prevarsum0_mv(len, v, missval, rsum, rsumw);
       varray_preskewsum_mv(len, v, missval, (rsum / rsumw), rsum3w, rsum3diff, rsum2diff);
@@ -1446,8 +1446,8 @@ varray_skew(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 }
 
 // Explicit instantiation
-template double varray_skew(size_t len, const Varray<float> &v, size_t nmiss, float missval);
-template double varray_skew(size_t len, const Varray<double> &v, size_t nmiss, double missval);
+template double varray_skew(size_t len, const Varray<float> &v, size_t numMissVals, float missval);
+template double varray_skew(size_t len, const Varray<double> &v, size_t numMissVals, double missval);
 
 #include <algorithm>
 
@@ -1460,7 +1460,8 @@ get_nth_element(T *array, size_t length, size_t n)
 }
 
 template <typename T>
-static double f_median(size_t len, Varray<T> &v)
+static double
+f_median(size_t len, Varray<T> &v)
 {
   if (len % 2 == 0)
     {
@@ -1478,7 +1479,7 @@ static double f_median(size_t len, Varray<T> &v)
 
 template <typename T>
 double
-varray_median(size_t len, const Varray<T> &v, size_t nmiss, T missval)
+varray_median(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
 {
   assert(len > 0);
   assert(v.size() > 0);
@@ -1486,7 +1487,7 @@ varray_median(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 
   double median = missval;
 
-  if (nmiss == 0)
+  if (numMissVals == 0)
     {
       Varray<T> v2 = v;
       median = f_median(len, v2);
@@ -1495,7 +1496,8 @@ varray_median(size_t len, const Varray<T> &v, size_t nmiss, T missval)
     {
       Varray<T> v2(len);
       size_t count = 0;
-      for (size_t i = 0; i < len; ++i) if (!dbl_is_equal(v[i], missval)) v2[count++] = v[i];
+      for (size_t i = 0; i < len; ++i)
+        if (!dbl_is_equal(v[i], missval)) v2[count++] = v[i];
       if (count > 0 && count < len) median = f_median(count, v2);
     }
 
@@ -1503,12 +1505,12 @@ varray_median(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 }
 
 // Explicit instantiation
-template double varray_median(size_t len, const Varray<float> &v, size_t nmiss, float missval);
-template double varray_median(size_t len, const Varray<double> &v, size_t nmiss, double missval);
+template double varray_median(size_t len, const Varray<float> &v, size_t numMissVals, float missval);
+template double varray_median(size_t len, const Varray<double> &v, size_t numMissVals, double missval);
 
 template <typename T>
 double
-varray_count(size_t len, const Varray<T> &v, size_t nmiss, T missval)
+varray_count(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
 {
   assert(len > 0);
   assert(v.size() > 0);
@@ -1516,7 +1518,7 @@ varray_count(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 
   size_t count = len;
 
-  if (nmiss > 0)
+  if (numMissVals > 0)
     {
       count = 0;
       for (size_t i = 0; i < len; ++i)
@@ -1529,5 +1531,5 @@ varray_count(size_t len, const Varray<T> &v, size_t nmiss, T missval)
 }
 
 // Explicit instantiation
-template double varray_count(size_t len, const Varray<float> &v, size_t nmiss, float missval);
-template double varray_count(size_t len, const Varray<double> &v, size_t nmiss, double missval);
+template double varray_count(size_t len, const Varray<float> &v, size_t numMissVals, float missval);
+template double varray_count(size_t len, const Varray<double> &v, size_t numMissVals, double missval);
diff --git a/src/varray.h b/src/varray.h
index 92376451564b0f39f7d37ef162647609fba704fe..d3c7bf82a9e146b3a8b1eb21c2b106f66f7d8083 100644
--- a/src/varray.h
+++ b/src/varray.h
@@ -8,12 +8,14 @@
 #define VARRAY_H
 
 #include <iostream>  // cerr
+#include <algorithm>
 #include <vector>
 #include <cstddef>
 #include <cfloat>
 #include <cassert>
 #include "compare.h"
 
+namespace ranges = std::ranges;
 
 // unary operators
 // clang-format off
@@ -53,7 +55,7 @@ const auto binary_op_MUL = [](double x, double y) noexcept { return x * y; };
 const auto binary_op_DIV = [](double x, double y) noexcept { return x / y; };
 // clang-format on
 
-//#define CHECK_UNUSED_VECTOR 1
+// #define CHECK_UNUSED_VECTOR 1
 
 #ifdef CHECK_UNUSED_VECTOR
 // clang-format off
@@ -208,10 +210,10 @@ using Matrix = std::array<std::array<T, COL>, ROW>;
 */
 
 template <typename T>
-MinMaxSum varray_min_max_sum(size_t len, const Varray<T> &v, MinMaxSum mms);
+MinMaxSum varray_min_max_sum(size_t len, const Varray<T> &v, const MinMaxSum &mms);
 
 template <typename T>
-MinMaxSum varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, MinMaxSum mms);
+MinMaxSum varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, const MinMaxSum &mms);
 
 template <typename T>
 MinMaxMean varray_min_max_mean(size_t len, const Varray<T> &v);
@@ -225,28 +227,6 @@ MinMax array_min_max_mask(size_t len, const T *array, const Varray<int> &mask);
 void array_add_array(size_t len, double *array1, const double *array2);
 void array_add_array_mv(size_t len, double *array1, const double *array2, double missval);
 
-template <typename T1, typename T2>
-void
-varray_fill(size_t len, T1 *v, T2 value)
-{
-  T1 c = value;
-  for (size_t i = 0; i < len; ++i) v[i] = c;
-}
-
-template <typename T1, typename T2>
-void
-varray_fill(Varray<T1> &v, T2 value)
-{
-  varray_fill(v.size(), v.data(), value);
-}
-
-template <typename T1, typename T2>
-void
-varray_fill(size_t len, Varray<T1> &v, T2 value)
-{
-  varray_fill(len, v.data(), value);
-}
-
 template <typename T1, typename T2>
 void
 array_copy(size_t len, const T1 *array1, T2 *array2)
@@ -277,7 +257,7 @@ varray_transform(const Varray<T> &vIn, Varray<T> &vOut, UnaryOperation unary_op)
   assert(vOut.size() > 0);
   assert(vOut.size() <= vIn.size());
 
-  const auto len = vIn.size();
+  auto len = vIn.size();
   for (size_t i = 0; i < len; ++i) vOut[i] = unary_op(vIn[i]);
 }
 
@@ -347,27 +327,27 @@ template <typename T>
 double varray_weighted_avg_mv(size_t len, const Varray<T> &v, const Varray<double> &w, T missval);
 
 template <typename T>
-double varray_var(size_t len, const Varray<T> &v, size_t nmiss, T missval);
+double varray_var(size_t len, const Varray<T> &v, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_var_1(size_t len, const Varray<T> &v, size_t nmiss, T missval);
+double varray_var_1(size_t len, const Varray<T> &v, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_weighted_var(size_t len, const Varray<T> &v, const Varray<double> &w, size_t nmiss, T missval);
+double varray_weighted_var(size_t len, const Varray<T> &v, const Varray<double> &w, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_weighted_var_1(size_t len, const Varray<T> &v, const Varray<double> &w, size_t nmiss, T missval);
+double varray_weighted_var_1(size_t len, const Varray<T> &v, const Varray<double> &w, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_skew(size_t len, const Varray<T> &v, size_t nmiss, T missval);
+double varray_skew(size_t len, const Varray<T> &v, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_kurt(size_t len, const Varray<T> &v, size_t nmiss, T missval);
+double varray_kurt(size_t len, const Varray<T> &v, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_median(size_t len, const Varray<T> &v, size_t nmiss, T missval);
+double varray_median(size_t len, const Varray<T> &v, size_t numMissVals, T missval);
 
 template <typename T>
-double varray_count(size_t len, const Varray<T> &v, size_t nmiss, T missval);
+double varray_count(size_t len, const Varray<T> &v, size_t numMissVals, T missval);
 
 #endif  //  VARRAY_H
diff --git a/src/verifygrid.h b/src/verifygrid.h
index ec9338281478c781c371f2cbf954b861377ec5c5..ca363282d0285943c7936fb27125d1dec7e9b213 100644
--- a/src/verifygrid.h
+++ b/src/verifygrid.h
@@ -1,6 +1,8 @@
 #ifndef VERIFYGRID_H
 #define VERIFYGRID_H
 
+#include "varray.h"
+
 struct Point
 {
   double x = 0.0, y = 0.0;
diff --git a/src/vertical_interp.cc b/src/vertical_interp.cc
index e862e81985c494b95bf2a2805754cc7a4580446a..c2c96de248e6cd530b463a425298ba83121b077b 100644
--- a/src/vertical_interp.cc
+++ b/src/vertical_interp.cc
@@ -285,8 +285,7 @@ template void vertical_interp_Z(const double *restrict geop, const double *restr
 
 template <typename T>
 static inline double
-vertical_interp_X_kernel(const T *restrict arrayIn, const T *restrict levels3D, long nl, double level, long ngp, long nhlev,
-                         double missval)
+vertical_interp_X_kernel(const T *arrayIn, const T *levels3D, long nl, double level, long ngp, long nhlev, double missval)
 {
   auto nh = nl + ngp;
   return (nl < 0) ? missval
@@ -297,38 +296,38 @@ vertical_interp_X_kernel(const T *restrict arrayIn, const T *restrict levels3D,
 
 template <typename T>
 void
-vertical_interp_X(const T *restrict arrayIn3D, T *arrayOut3D, const T *levels3D, const int *vertIndex3D,
-                  const double *restrict levels, long numLevels, long ngp, long nhlev, double missval)
+vertical_interp_X(const T *restrict arrayIn3D, T *restrict arrayOut3D, const T *levels3D, const int *vertIndex3D,
+                  const double *restrict levels, long numLevels, long numGP, long nhlev, double missval)
 {
   if (numLevels > 3)
     {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
-      for (long lp = 0; lp < numLevels; lp++)
+      for (long levIndex = 0; levIndex < numLevels; levIndex++)
         {
-          auto level = levels[lp];
-          const int *restrict vertIndex = vertIndex3D + lp * ngp;
-          auto arrayOut = arrayOut3D + lp * ngp;
-          for (long i = 0; i < ngp; ++i)
+          auto level = levels[levIndex];
+          const int *restrict vertIndex = vertIndex3D + levIndex * numGP;
+          auto *restrict arrayOut2D = arrayOut3D + levIndex * numGP;
+          for (long i = 0; i < numGP; ++i)
             {
-              arrayOut[i] = vertical_interp_X_kernel(arrayIn3D, levels3D, vertIndex[i] * ngp + i, level, ngp, nhlev, missval);
+              arrayOut2D[i] = vertical_interp_X_kernel(arrayIn3D, levels3D, vertIndex[i] * numGP + i, level, numGP, nhlev, missval);
             }
         }
     }
   else
     {
-      for (long lp = 0; lp < numLevels; lp++)
+      for (long levIndex = 0; levIndex < numLevels; levIndex++)
         {
-          auto level = levels[lp];
-          const int *restrict vertIndex = vertIndex3D + lp * ngp;
-          auto *restrict arrayOut = arrayOut3D + lp * ngp;
+          auto level = levels[levIndex];
+          const int *restrict vertIndex = vertIndex3D + levIndex * numGP;
+          auto *restrict arrayOut2D = arrayOut3D + levIndex * numGP;
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
-          for (long i = 0; i < ngp; ++i)
+          for (long i = 0; i < numGP; ++i)
             {
-              arrayOut[i] = vertical_interp_X_kernel(arrayIn3D, levels3D, vertIndex[i] * ngp + i, level, ngp, nhlev, missval);
+              arrayOut2D[i] = vertical_interp_X_kernel(arrayIn3D, levels3D, vertIndex[i] * numGP + i, level, numGP, nhlev, missval);
             }
         }
     }
@@ -336,16 +335,16 @@ vertical_interp_X(const T *restrict arrayIn3D, T *arrayOut3D, const T *levels3D,
 
 // Explicit instantiation
 template void vertical_interp_X(const float *restrict arrayIn3D, float *arrayOut3D, const float *levels3D, const int *vertIndex3D,
-                                const double *levels, long numLevels, long ngp, long nhlev, double missval);
+                                const double *levels, long numLevels, long numGP, long nhlev, double missval);
 template void vertical_interp_X(const double *restrict arrayIn3D, double *arrayOut3D, const double *levels3D,
-                                const int *vertIndex3D, const double *levels, long numLevels, long ngp, long nhlev, double missval);
+                                const int *vertIndex3D, const double *levels, long numLevels, long numGP, long nhlev, double missval);
 
 template <typename T>
 void
 gen_vert_index(int *vertIndex, const double *restrict plev, const T *restrict levels3D, long ngp, long nplev, long nhlev,
                bool lreverse)
 {
-  varray_fill(ngp * nplev, vertIndex, 0);
+  ranges::fill_n(vertIndex, ngp * nplev, 0);
 
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
@@ -384,14 +383,14 @@ template void gen_vert_index(int *vertIndex, const double *plev, const double *l
 template <typename T>
 void
 gen_vert_index_mv(int *vertIndex, const double *restrict plev, long ngp, long nplev, const T *restrict psProg,
-                  size_t *restrict pnmiss, bool lreverse)
+                  size_t *restrict pnumMissVals, bool lreverse)
 {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
   for (long lp = 0; lp < nplev; lp++)
     {
-      pnmiss[lp] = 0;
+      pnumMissVals[lp] = 0;
       T pres = plev[lp];
       auto *restrict vertIndexLev = vertIndex + lp * ngp;
 
@@ -402,7 +401,7 @@ gen_vert_index_mv(int *vertIndex, const double *restrict plev, long ngp, long np
               if (pres < psProg[i])
                 {
                   vertIndexLev[i] = -1;
-                  pnmiss[lp]++;
+                  pnumMissVals[lp]++;
                 }
             }
         }
@@ -413,7 +412,7 @@ gen_vert_index_mv(int *vertIndex, const double *restrict plev, long ngp, long np
               if (pres > psProg[i])
                 {
                   vertIndexLev[i] = -1;
-                  pnmiss[lp]++;
+                  pnumMissVals[lp]++;
                 }
             }
         }
@@ -421,7 +420,7 @@ gen_vert_index_mv(int *vertIndex, const double *restrict plev, long ngp, long np
 }
 
 // Explicit instantiation
-template void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const float *psProg, size_t *pnmiss,
+template void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const float *psProg, size_t *pnumMissVals,
                                 bool lreverse);
-template void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const double *psProg, size_t *pnmiss,
+template void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const double *psProg, size_t *pnumMissVals,
                                 bool lreverse);
diff --git a/src/vertical_interp.h b/src/vertical_interp.h
index 64e1a70cb284dbc0bb918b26f82533e9643bf65e..7569a3e53e3038895785edf0371c1be984497b72 100644
--- a/src/vertical_interp.h
+++ b/src/vertical_interp.h
@@ -26,14 +26,14 @@ void vertical_interp_Z(const T *geop, const T *gz, T *pz, const T *fullPress, co
 
 template <typename T>
 void vertical_interp_X(const T *arrayIn3D, T *arrayOut3D, const T *levels3D, const int *vertIndex3D, const double *levels,
-                       long numLevels, long ngp, long nhlev, double missval);
+                       long numLevels, long numGP, long nhlev, double missval);
 
 template <typename T>
 void gen_vert_index(int *vertIndex, const double *plev, const T *fullPress, long ngp, long nplev, long nhlev,
                     bool lreverse = false);
 
 template <typename T>
-void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const T *psProg, size_t *pnmiss,
+void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const T *psProg, size_t *pnumMissVals,
                        bool lreverse = false);
 
 #endif /* VERTICAL_INTERP_H */
diff --git a/test/bandit_tests/Makefile.am b/test/bandit_tests/Makefile.am
index fa64a462d29f7f63becac5e0d5f0b0e70227d093..88c43b4e6ffe19804be19cc2cb5d8ccd189848d9 100644
--- a/test/bandit_tests/Makefile.am
+++ b/test/bandit_tests/Makefile.am
@@ -1,4 +1,12 @@
-noinst_PROGRAMS = wildcards Seltime_test param_conversion_test module_definitions process_init module_interface parser string_util
+noinst_PROGRAMS = wildcards\
+				  Seltime_test\
+				  param_conversion_test\
+				  module_definitions\
+				  process_init\
+				  module_interface\
+				  parser\
+				  string_util\
+				  pmlist
 
 tests_CPPFLAGS = -I$(top_srcdir)/test/bandit_tests -I$(top_srcdir)/test/bandit_tests/bandit -I$(top_srcdir)/libcdi/src -std=c++20
 tests_LDADD = $(top_builddir)/src/libcdo.la $(top_builddir)/libcdi/src/libcdi.la
@@ -8,6 +16,10 @@ parser_SOURCES = parser.cc
 parser_CPPFLAGS = $(tests_CPPFLAGS)
 parser_LDADD = $(tests_LDADD)
 
+pmlist_SOURCES = pmlist.cc
+pmlist_CPPFLAGS = $(tests_CPPFLAGS)
+pmlist_LDADD = $(tests_LDADD)
+
 param_conversion_test_SOURCES = param_conversion_test.cc
 param_conversion_test_CPPFLAGS = $(tests_CPPFLAGS)
 param_conversion_test_LDADD = $(tests_LDADD)
@@ -37,4 +49,13 @@ string_util_CPPFLAGS = $(tests_CPPFLAGS)
 string_util_LDADD = $(tests_LDADD)
 
 LD_LIBRARY_PATH = $(tests_LDADD)
-TESTS = ./parser ./wildcards ./Seltime_test ./param_conversion_test ./module_definitions ./process_init ./module_interface ./string_util
+TESTS=./parser\
+	  ./wildcards\
+	  ./Seltime_test\
+	  ./param_conversion_test\
+	  ./module_definitions\
+	  ./process_init\
+	  ./module_interface\
+	  ./string_util\
+	  ./pmlist
+
diff --git a/test/bandit_tests/module_definitions.cc b/test/bandit_tests/module_definitions.cc
index b08200da4c408e39281dd05bda9a9ad534928162..4f0aae3c1ad31331d9af9dee61d20a34ebbcc964 100644
--- a/test/bandit_tests/module_definitions.cc
+++ b/test/bandit_tests/module_definitions.cc
@@ -2,51 +2,33 @@
 // BANDIT NEEDS TO BE INCLUDED FIRST!!!
 
 #include <iostream>
-#include "../../src/modules.h"
+#include <vector>
 
-bool is_alias(const std::string &);
+#include "../../src/modules.h"
+#include "../../src/factory.h"
+#include "../../src/cdo_module.h"
 
-const std::string alias_name = "alias1-1";
-void *
-testFunction(void *test)
-{
-  return test;
-}
-static const module_t module_with_alias = { "aliasModule",
-                                            testFunction,
-                                            {},
-                                            { "oper1-1", "oper1-2" },
-                                            1,
-                                            0,
-                                            1,
-                                            1,
-                                            NoRestriction,
-                                            { Alias(alias_name, "oper1-1") } };
+#include "test_module_list.h"
 
 using namespace snowhouse;
 
 go_bandit([]() {
   bandit::describe("Testing for registered module with alias", [&]() {
-    bandit::it("has registered the module",
-               [&]() { AssertThat(get_modules(), Is().OfLength(1)); });
-    bandit::it(
-        "has added both operators to module map while not adding the alias",
-        [&]() { AssertThat(get_module_map(), Is().OfLength(2)); });
-    bandit::it("has registered the alias",
-               [&]() { AssertThat(get_aliases(), Is().OfLength(1)); });
-    bandit::it("has registered the correct original name", [&]() {
-      AssertThat(get_aliases()[alias_name], Is().EqualTo("oper1-1"));
+    bandit::it("has registered the module", [&]() {
+      AssertThat(Factory::get().size(), Is().EqualTo(13ul));
+    });
+  });
+  bandit::describe("Testing if the help was added", [&]() {
+    bandit::it("and it can be retrieved", [&]() {
+      const CdoHelp expected_container = { "dummy_help" };
+      AssertThat(Factory::get_help("oper1-1"),
+                 EqualsContainer(expected_container));
     });
   });
   bandit::describe("Testing the interface for alias and name retreval", [&]() {
     bandit::it("gets the right name for alias", [&]() {
-      AssertThat(get_original(alias_name.c_str()), Is().EqualTo("oper1-1"));
-    });
-    bandit::it("extracts the operator name", [&]() {
-      AssertThat(get_original("oper1-1"), Is().EqualTo("oper1-1"));
+      AssertThat(Factory::get_original(alias_name), Is().EqualTo("oper1-1"));
     });
-    bandit::it("recognizes an alias",
-               [&]() { AssertThat(is_alias(alias_name), IsTrue()); });
   });
 });
 
diff --git a/test/bandit_tests/module_interface.cc b/test/bandit_tests/module_interface.cc
index 0c1348dd6c5233c05ffc291a0d1c43cdd1f2f3c2..856e69c02222977b08da689a3dfd2f3ba88b6bd1 100644
--- a/test/bandit_tests/module_interface.cc
+++ b/test/bandit_tests/module_interface.cc
@@ -4,41 +4,11 @@
 
 #include <iostream>
 #include "../../src/modules.h"
+#include "../../src/parser.h"
 
 using namespace snowhouse;
 
 go_bandit([]() {
-  bandit::describe(
-      "Testing operator name extraction from command string", [&]() {
-        bandit::it("returns the commandString if it and has no '-' and has no "
-                   "arguments",
-                   [&]() {
-                     AssertThat(extract_operator_name("test"),
-                                Is().EqualTo("test"));
-                   });
-
-        bandit::it("returns the commandString without the '-' while it has no "
-                   "arguments",
-                   [&]() {
-                     AssertThat(extract_operator_name("-test"),
-                                Is().EqualTo("test"));
-                   });
-
-        bandit::it(
-            "returns the commandString without the '-' while it has arguments",
-            [&]() {
-              AssertThat(extract_operator_name("-test,arg"),
-                         Is().EqualTo("test"));
-            });
-
-        bandit::it(
-            "returns the commandString while it has arguments and has no '-'",
-            [&]() {
-              AssertThat(extract_operator_name("test,arg"),
-                         Is().EqualTo("test"));
-            });
-      });
-
   bandit::describe(
       "Testing operator name and argument extraction from command string",
       [&]() {
@@ -47,7 +17,7 @@ go_bandit([]() {
                    [&]() {
                      std::string operName;
                      std::string operArgument;
-                     extract_name_and_argument("-test,arg", operName,
+                     Parser::Util::extract_name_and_argument("-test,arg", operName,
                                                operArgument);
                      AssertThat(operName, Is().EqualTo("test"));
                      AssertThat(operArgument, Is().EqualTo("arg"));
@@ -55,7 +25,7 @@ go_bandit([]() {
         bandit::it("does not cut off multiple arguments with no '-'", [&]() {
           std::string operName;
           std::string operArgument;
-          extract_name_and_argument("test,arg,arg2,arg3", operName,
+          Parser::Util::extract_name_and_argument("test,arg,arg2,arg3", operName,
                                     operArgument);
           AssertThat(operName, Is().EqualTo("test"));
           AssertThat(operArgument, Is().EqualTo("arg,arg2,arg3"));
@@ -66,7 +36,7 @@ go_bandit([]() {
             [&]() {
               std::string operName;
               std::string operArgument;
-              extract_name_and_argument("test", operName, operArgument);
+              Parser::Util::extract_name_and_argument("test", operName, operArgument);
               AssertThat(operName, Is().EqualTo("test"));
               AssertThat(operArgument, Is().EqualTo(""));
             });
@@ -75,7 +45,7 @@ go_bandit([]() {
                    [&]() {
                      std::string operName;
                      std::string operArgument;
-                     extract_name_and_argument("-test", operName, operArgument);
+                     Parser::Util::extract_name_and_argument("-test", operName, operArgument);
                      AssertThat(operName, Is().EqualTo("test"));
                      AssertThat(operArgument, Is().EqualTo(""));
                    });
diff --git a/test/bandit_tests/parser.cc b/test/bandit_tests/parser.cc
index b1e6adc10ba2cb0cdb61c26d337329814276d141..031cca553f63193e8ebb60d6033d492aa6e9074e 100644
--- a/test/bandit_tests/parser.cc
+++ b/test/bandit_tests/parser.cc
@@ -12,10 +12,11 @@
 #include "../../src/modules.h"
 #include "../../src/process_int.h"
 #include "../../src/cdo_options.h"
-#include "test_module_list.h"
 #include "../../src/cdo_node_attach_exception.h"
 #include "../../src/cdo_exception.h"
 
+#include "test_module_list.h"
+
 // Util stuff tests start at marker: TESTS
 using namespace snowhouse;
 
@@ -25,7 +26,7 @@ cdoExit()
 {
 }
 const char *
-process_inq_prompt(void)
+test_process_inq_prompt(void)
 {
   static const char *context = "cdo_test";
   return context;
@@ -72,7 +73,7 @@ check(std::string description, std::vector<std::string> in, std::string out)
     unsigned numChildrenExpected = -1337;
     try
       {
-        auto res = Parser::parse(in, process_inq_prompt);
+        auto res = Parser::parse(in, test_process_inq_prompt);
         AssertThat(res, Is().OfLength(1));
         node_structure = res[0]->to_string();
         numChildrenExpected = getNumChildren(res[0]);
@@ -108,7 +109,7 @@ checkApply(std::string description, std::vector<std::string> in,
     unsigned numChildrenExpected;
     try
       {
-        auto res = Parser::parse(in, process_inq_prompt);
+        auto res = Parser::parse(in, test_process_inq_prompt);
         if (res.size() > 0)
           {
             node_structure = res[0]->to_string();
@@ -136,7 +137,7 @@ go_bandit([]() {
   //==============================================================================
   cdo::progname = "cdo_bandit_test";
   cdo::set_exit_function(cdoExit);
-  cdo::set_context_function(process_inq_prompt);
+  cdo::set_context_function(test_process_inq_prompt);
 
   // cdo::set_debug(1024);
   //-----------------------------Test_01------------------------------------------
@@ -377,6 +378,9 @@ go_bandit([]() {
       checkNegative("Missing inputs are detected in top most parser",
                     { "-in1_out1", "-in1_out1", "out" },
                     Parser::errmsg_missing_inputs);
+      checkNegative("OnlyFirst operators are detected in other positions",
+                    { "-in1_out1","-only_first_oper", "-in0_out1", "out" },
+                    Parser::errmsg_not_in_first_position);
     });
     bandit::describe("Apply Errors:", [&]() {
       checkNegative("detects arguments with multiple inputs ",
diff --git a/test/bandit_tests/pmlist.cc b/test/bandit_tests/pmlist.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9f89cee4855dd8fb40ed4953df4abc7d7e3c968f
--- /dev/null
+++ b/test/bandit_tests/pmlist.cc
@@ -0,0 +1,194 @@
+// bandit needs to be included!!!
+
+#include "bandit/bandit/bandit.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <stdlib.h>
+#include "../../src/pmlist.h"
+
+class KVTest
+{
+public:
+  std::string msg = "UNINITALIZED";
+  std::vector<std::string> input = {};
+  int retval = -2;
+  size_t num_keys = 0;
+  std::vector<size_t> num_vars = {};
+  std::map<std::string, std::vector<std::string>> expected = {};
+};
+
+void
+check_key_and_values(
+    const std::pair<const std::string, std::vector<std::string>> &expected,
+    KVList &KV)
+{
+  bandit::it(KV.name + " has the right values in the right keys", [&] {
+    const KeyValues *ptr = KV.search(expected.first);
+
+    AssertThat(ptr, snowhouse::Is().Not().Null());
+    AssertThat(expected.second.size(),
+               snowhouse::Is().EqualTo(ptr->values.size()));
+
+    for (size_t i = 0; i < ptr->values.size(); i++)
+      {
+        AssertThat(ptr->values[i], snowhouse::Is().EqualTo(expected.second[i]));
+      }
+  });
+}
+
+void
+main_check(const KVTest &curTest, KVList &test_obj)
+{
+
+  bandit::describe(test_obj.name, [&] {
+    int retVal = -2;
+    bandit::describe("runs and  (" + std::to_string(curTest.retval) + ")", [&] {
+      retVal = test_obj.parse_arguments(curTest.input);
+      bandit::it("returns the right exit code", [&] {
+        AssertThat(retVal, snowhouse::Is().EqualTo(curTest.retval));
+      });
+      bandit::it("has the right number of keys", [&] {
+        AssertThat(test_obj.size(), snowhouse::Is().EqualTo(curTest.num_keys));
+      });
+    });
+
+    for (auto &expected : curTest.expected)
+      {
+        check_key_and_values(expected, test_obj);
+      }
+  });
+}
+
+// TESTS
+go_bandit([]() {
+  std::vector<KVTest> tests
+      = { { .msg = "handles mutliple keys with multiple values",
+            .input = { "key1=1.0", "2.0", "key2=2.0", "2.2" },
+            .retval = 0,
+            .num_keys = 2,
+            .num_vars = { 2, 2 },
+            .expected
+            = { { "key1", { "1.0", "2.0" } }, { "key2", { "2.0", "2.2" } } } },
+
+          { .msg = "handles missing value after '='",
+            .input = { "key1=" },
+            .retval = 0,
+            .num_keys = 1,
+            .expected = {} },
+
+          { .msg = "handles missing value after '=' in the second argument",
+            .input = { "key1=1.0", "key2=" },
+            .retval = 0,
+            .num_keys = 2,
+            .expected = { { "key1", { "1.0" } }, { "key2", {} } } },
+
+          { .msg = "handles missing '='",
+            .input = { "key12.0" },
+            .retval = -1,
+            .num_keys = 0,
+            .expected = {} },
+
+          { .msg = "handles missing '=' in the second value",
+            .input = { "key=12.0", "key2" },
+            .retval = 0,
+            .num_keys = 1,
+            .expected = { { "key", { "12.0", "key2" } } } } };
+
+  bandit::describe("KvList parse_arguments", [&]() {
+    for (auto &curTest : tests)
+      {
+        bandit::describe(curTest.msg, [&] {
+          KVList kv;
+          kv.name = "parse_arguments test";
+          main_check(curTest, kv);
+        });
+      }
+  });
+
+  bandit::describe("KvList search", [&]() {
+    KVList kvlist;
+    auto test = tests[0];
+    auto retVal = kvlist.parse_arguments(test.input);
+    if (retVal != 0)
+      {
+        AssertThat(retVal, snowhouse::Is().EqualTo(0));
+      }
+    bandit::describe("KvList search returns correct result", [&]() {
+      auto resKey1 = kvlist.search("key1");
+      bandit::describe("finds both keys and the correct values", [&]() {
+        bandit::it("finds the first keys", [&]() {
+          AssertThat(resKey1->key, snowhouse::Is().EqualTo("key1"));
+        });
+        bandit::it("contains the right values", [&]() {
+          AssertThat(resKey1->values,
+                     snowhouse::EqualsContainer(test.expected["key1"]));
+        });
+        auto resKey2 = kvlist.search("key2");
+        bandit::it("finds the second keys", [&]() {
+          AssertThat(resKey2->key, snowhouse::Is().EqualTo("key2"));
+        });
+        bandit::it("contains the right values", [&]() {
+          AssertThat(resKey2->values,
+                     snowhouse::EqualsContainer(test.expected["key2"]));
+        });
+      });
+    });
+  });
+
+  bandit::describe("KvList append", [&]() {
+    KVList kvlist;
+    std::vector<std::string> to_be_inserted = { "1", "2" };
+    std::vector<std::string> to_be_appended = { "3", "4" };
+    std::string key = "key1";
+    bandit::it("adds the values to a new key when no key was found",
+               [&]() { kvlist.append(key, to_be_inserted); });
+    bandit::it("adds the values to the right key",
+               [&]() { kvlist.append(key, to_be_appended); });
+  });
+
+  bandit::describe("KvList append", [&]() {
+    KVList kvlist;
+    std::string key = "key1";
+    std::string false_key = "does_not_exist";
+
+    auto retVal = kvlist.parse_arguments(tests[0].input);
+    (void) retVal;
+    bandit::describe("removes given key", [&]() {
+      bandit::it("has size 2 before remove", [&]() {
+        AssertThat(kvlist.size(), snowhouse::Is().EqualTo(tests[0].num_keys));
+      });
+      kvlist.remove(key);
+      bandit::it("does no longer contain the key:" + key, [&]() {
+        AssertThat(kvlist.search(key), snowhouse::Is().Null());
+      });
+      bandit::it("has size 1 after remove", [&]() {
+        AssertThat(kvlist.size(), snowhouse::Is().EqualTo(1ul));
+      });
+    });
+    bandit::describe("can handle removing a non existing key", [&]() {
+      auto before = kvlist.size();
+      kvlist.remove(false_key);
+      auto after = kvlist.size();
+      bandit::it("did not insert another key on accident", [&]() {
+        AssertThat(kvlist.search(false_key), snowhouse::Is().Null());
+      });
+      bandit::it("has not removed anything (size is the same)",
+                 [&]() { AssertThat(before, snowhouse::Is().EqualTo(after)); });
+    });
+  });
+});
+#define EXCEPTION_EXTRA_INFO = 1;
+
+int
+main(int argc, char **argv)
+{
+  std::vector<char *> argv_v(argv, argv + argc);
+  std::string reporter = "--reporter=spec";
+  argv_v.push_back(&reporter[0]);
+  int result = bandit::run(argc + 1, argv_v.data());
+
+  return result;
+}
diff --git a/test/bandit_tests/process_init.cc b/test/bandit_tests/process_init.cc
index 0f08579af7f68bb3f2cd986c96ff033c6750ec22..6b2c89ef531109ba49672f9ecf0ccf48cd7554b6 100644
--- a/test/bandit_tests/process_init.cc
+++ b/test/bandit_tests/process_init.cc
@@ -4,43 +4,48 @@
 
 #include <iostream>
 #include <memory>
+#include <vector>
+#include <string>
+#include <iostream>
 
-#include "../../src/modules.h"
+#include "../../src/cdo_module.h"
 #include "../../src/process.h"
-const std::string alias_name = "alias1-1";
-void *
-testFunction(void *test)
-{
-  return test;
-}
-
-static const module_t module_with_alias = { "aliasModule",
-                                            testFunction,
-                                            {},
-                                            { "oper1-1", "oper1-2" },
-                                            1,
-                                            0,
-                                            1,
-                                            1,
-                                            NoRestriction,
-                                            { Alias(alias_name, "oper1-1") } };
+#include "../../src/factory.h"
+#include "test_module_list.h"
 
 using namespace snowhouse;
 
 go_bandit([]() {
   cdo::progname = "process_init_test";
-  std::vector<std::string> arguements;
-  auto shared_process = std::make_shared<Process>(0, alias_name, arguements);
-
-  bandit::it(
-      "assignes the origial of the alias as operatorName in init of process",
-      [&]() {
-        AssertThat(shared_process->operatorName, Is().EqualTo("oper1-1"));
-      });
-
-  bandit::it("creates the right prompt, discarding the alias in favor of the original name", [&]() {
-    AssertThat(shared_process->prompt,
-               Is().EqualTo(std::string(cdo::progname) + "    " + shared_process->operatorName));
+  std::vector<std::string> arguments = {};
+  auto constructor_wrapper = Factory::find(alias_name);
+
+  bandit::it("factory contains operators", [&]() {
+    AssertThat(Factory::get().size(), Is().GreaterThan(0ul));
+    bandit::it("found the alias", [&]() {
+        auto &factory = Factory::get();
+      AssertThat(constructor_wrapper != factory.end(),
+                 Is().EqualTo(true));
+
+      auto shared_process
+          = constructor_wrapper->second.constructor(0, alias_name, arguments);
+
+      bandit::it("assignes the origial of the alias as operatorName in init of "
+                 "process",
+                 [&]() {
+                   AssertThat(shared_process->operatorName,
+                              Is().EqualTo("oper1-1"));
+                 });
+
+      bandit::it(
+          "creates the right prompt, discarding the alias in favor of the "
+          "original name",
+          [&]() {
+            AssertThat(shared_process->prompt,
+                       Is().EqualTo(std::string(cdo::progname) + "    "
+                                    + shared_process->operatorName));
+          });
+    });
   });
 });
 
diff --git a/test/bandit_tests/test_module_list.h b/test/bandit_tests/test_module_list.h
index 9b6675f41c28cc56a5ca08c29e6713d12a3b9423..30cb82c99f20c6d44238bbc4a10a761adeb94a99 100644
--- a/test/bandit_tests/test_module_list.h
+++ b/test/bandit_tests/test_module_list.h
@@ -1,24 +1,183 @@
 #ifndef TEST_MODULE_LIST_H
 #define TEST_MODULE_LIST_H
 #include "../../src/modules.h"
+#include "../../src/process.h"
+#include "../../src/cdo_module.h"
+
+const CdoHelp dummy_help = { "dummy_help" };
+
+class DummyProcess : public Process
+{
+public:
+  using Process::Process;
+  void init(){};
+  void run(){};
+  void close(){};
+};
+
+const std::string alias_name = "alias1-1";
+
+class TestModule : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module = {
+    .name = "TestModule",
+    .operators = { { "oper1-1", 0, 1, "constantvalue", dummy_help },
+                   { "oper1-2", 0, 1, "constantvalue", dummy_help } },
+    .aliases = { Alias("alias1-1", "oper1-1") },
+    .mode = 0,           // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, NoRestriction },
+  };
+  inline static RegisterEntry<TestModule> registration
+      = RegisterEntry<TestModule>(module);
+};
+
+class OnlyFirstModule : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module = {
+    .name = "TestModule",
+    .operators = { { "only_first_oper", 0, 1, "constantvalue", dummy_help } },
+    .aliases = {},
+    .mode = 0,           // Module mode: 0:intern 1:extern
+    .number = CDI_BOTH,  // Allowed number type
+    .constraints = { 1, 1, OnlyFirst },
+  };
+  inline static RegisterEntry<OnlyFirstModule> registration
+      = RegisterEntry<OnlyFirstModule>(module);
+};
+
+class Module_in0_out1 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "in0_out1",
+          .operators = { { "in0_out1", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 0, 1, NoRestriction } };
+
+  inline static RegisterEntry<Module_in0_out1> registration
+      = RegisterEntry<Module_in0_out1>(module);
+};
+class Module_in1_out0 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "in1_out0",
+          .operators = { { "in1_out0", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 1, 0, NoRestriction } };
+
+  inline static RegisterEntry<Module_in1_out0> registration
+      = RegisterEntry<Module_in1_out0>(module);
+};
+class Module_in1_out1 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "in1_out1",
+          .operators = { { "in1_out1", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 1, 1, NoRestriction } };
+
+  inline static RegisterEntry<Module_in1_out1> registration
+      = RegisterEntry<Module_in1_out1>(module);
+};
+class Module_in2_out1 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "in2_out1",
+          .operators = { { "in2_out1", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 2, 1, NoRestriction } };
+  inline static RegisterEntry<Module_in2_out1> registration
+      = RegisterEntry<Module_in2_out1>(module);
+};
+class Module_in2_outObase : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "in2_outObase",
+          .operators = { { "in2_outObase", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 2, -1, NoRestriction } };
+  inline static RegisterEntry<Module_in2_outObase> registration
+      = RegisterEntry<Module_in2_outObase>(module);
+};
+class Module_in2_out0 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "in2_out0",
+          .operators = { { "in2_out0", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 2, 0, NoRestriction } };
+  inline static RegisterEntry<Module_in2_out0> registration
+      = RegisterEntry<Module_in2_out0>(module);
+};
+class Module_inVariable_out0 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "inVariable_out0",
+          .operators = { { "inVariable_out0", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { -1, 0, NoRestriction } };
+  inline static RegisterEntry<Module_inVariable_out0> registration
+      = RegisterEntry<Module_inVariable_out0>(module);
+};
+class Module_inVariable_out1 : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "inVariable_out1",
+          .operators = { { "inVariable_out1", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { -1, 1, NoRestriction } };
+  inline static RegisterEntry<Module_inVariable_out1> registration
+      = RegisterEntry<Module_inVariable_out1>(module);
+};
+class Module_files_only : public DummyProcess
+{
+public:
+  using DummyProcess::DummyProcess;
+  inline static CdoModule module
+      = { .name = "files_only",
+          .operators = { { "files_only", 0, 0, nullptr, dummy_help } },
+          .aliases = {},
+          .mode = 1,
+          .number = 0,
+          .constraints = { 1, 1, FilesOnly } };
+  inline static RegisterEntry<Module_files_only> registration
+      = RegisterEntry<Module_files_only>(module);
+};
 
-void *in0_out1(void *test) { return test; }
-void *in1_out0(void *test) { return test; }
-void *in1_out1(void *test) { return test; }
-void *in2_out1(void *test) { return test; }
-void *in2_out0(void *test) { return test; }
-void *in2_outObase(void *test) { return test; }
-void *inVariable_out0(void *test) { return test; }
-void *inVariable_out1(void *test) { return test; }
-void *file_only(void *test) { return test; }
-
-static const module_t  module_in0_out1 = {"in0_out1",in0_out1, {}, { "in0_out1" }, 1, 0, 0, 1, NoRestriction };
-static const module_t  module_in1_out0 = {"in1_out0",in0_out1, {}, { "in1_out0" }, 1, 0, 1, 0, NoRestriction };
-static const module_t  module_in1_out1 = {"in1_out1",in1_out1, {}, { "in1_out1" }, 1, 0, 1, 1, NoRestriction };
-static const module_t  module_in2_out1 = {"in2_out1",in2_out1, {}, { "in2_out1" }, 1, 0, 2, 1, NoRestriction };
-static const module_t  module_in2_outObase = {"in2_outObase",in2_outObase, {}, { "in2_outObase" }, 1, 0, 2, -1, NoRestriction };
-static const module_t  module_in2_out0 = {"in2_out0",in2_out0, {}, { "in2_out0" }, 1, 0, 2, 0, NoRestriction };
-static const module_t  module_inVariable_out0 = {"inVariable_out0", inVariable_out0, {}, { "inVariable_out0" }, 1, 0, -1, 0, NoRestriction };
-static const module_t  module_inVariable_out1 = {"inVariable_out1",inVariable_out1, {}, { "inVariable_out1" }, 1, 0, -1, 1, NoRestriction };
-static const module_t  module_files_only = {"files_only",file_only, {}, { "files_only" }, 1, 0, 1, 1, FilesOnly };
 #endif
diff --git a/test/bandit_tests/util_string.cc b/test/bandit_tests/util_string.cc
index 3cf02fd17cc3c8fbb12afd209b20c89a919de602..bf424731df15a04ec066ced2e94cbea298e082f4 100644
--- a/test/bandit_tests/util_string.cc
+++ b/test/bandit_tests/util_string.cc
@@ -73,6 +73,32 @@ go_bandit([]() {
                            EqualsContainer(expected_tokens));
               });
             });
+        bandit::describe("Testing trim functions", []() {
+          bandit::describe("Testing ltrim function", []() {
+            std::string to_be_trimmed = "  spaces in front  ";
+            std::string expected = "spaces in front  ";
+            bandit::it("returns the right trim", [&]() {
+              std::string result = Util::String::ltrim(to_be_trimmed);
+              AssertThat(result, Equals(expected));
+            });
+          });
+          bandit::describe("Testing rtrim function", []() {
+            std::string to_be_trimmed = "  spaces in back  ";
+            std::string expected = "  spaces in back";
+            bandit::it("returns the right trim", [&]() {
+              std::string result = Util::String::rtrim(to_be_trimmed);
+              AssertThat(result, Equals(expected));
+            });
+          });
+          bandit::describe("Testing trim function", []() {
+            std::string to_be_trimmed = "  no spaces  ";
+            std::string expected = "no spaces";
+            bandit::it("returns the right trim", [&]() {
+              std::string result = Util::String::trim(to_be_trimmed);
+              AssertThat(result, Equals(expected));
+            });
+          });
+        });
       });
 
   bandit::describe("Testing function 'tokenize_comma_seperated_int_list'",
diff --git a/test/bm_Remapcon.sh b/test/bm_Remapcon.sh
index ac142a1d2a56293f94646128ce124ee1aadd197f..ee5f1f8ffee5d7d8797950ddb65be22193f88ef0 100755
--- a/test/bm_Remapcon.sh
+++ b/test/bm_Remapcon.sh
@@ -11,7 +11,7 @@ for N in 3 4 5 6 7 8 9; do
   $CDO -f nc4 topo,icor2b$N $IFILE
   OFILE=${IFILE}ycon
   echo "process $IFILE"
-  time $CDO -P 4  remapcon,global_0.2 $IFILE ${OFILE}
+  time $CDO -P 1  remapcon,global_0.2 $IFILE ${OFILE}
 done
 
 #1 levante: gcc 11.2.0 avx2 (interactiv)  cdo-2.0.5
@@ -74,6 +74,20 @@ done
 #01     7   7   8   10   14    24     59
 #04     2   2   3    3    5     8     21
 
+# CDO 2.4.0 (YAC3.0.1)
+#
+#1 hama: clang 16.0.6 avx2
+#       3   4   5    6    7     8      9     10
+#01    10  11  12   15   23    43    125
+#04     3   4   4    5    7    15     44
+
+# CDO 2.4.0 (YAC3.1)
+#
+#1 hama: clang 16.0.6 avx2
+#       3   4   5    6    7     8      9     10
+#01     9  10  12   14   21    40    100
+#04     3   3   4    5    7    13     38
+
 #1 mistral: intel 18.0.1 avx2 (interactiv on mg208 node)
 # CDO version 1.9.8  -P 16
 #           ncells        remapcon       remapnn
diff --git a/test/data/expr1_ref b/test/data/expr1_ref
index 64f37005a2b2e49f97c65f872845a0800d6908dc..c19fb7bd7bf46c978f2d099386400ab52c781b8d 100644
Binary files a/test/data/expr1_ref and b/test/data/expr1_ref differ
diff --git a/test/data/gen_refdata.sh b/test/data/gen_refdata.sh
index 8ca9ccd58397edde383bf996980f83a1bbaea219..e6cc93c47c47b1ffff27e6a44f810679229c6fca 100755
--- a/test/data/gen_refdata.sh
+++ b/test/data/gen_refdata.sh
@@ -552,12 +552,12 @@ exit
 # TYPE=20 CODE=129,130,152,156,157
 #EOF
 #
-IFILE=afterdata
+IFILE=afterdata.grb
 OFILE=hl_l19.grb
 $CDO fldmean $IFILE $OFILE
 IFILE=$OFILE
-OFILE=ml2pl_ref
-$CDO $FORMAT ml2pl,92500,85000,50000,20000 $IFILE $OFILE
+OFILE=ml2plx_ref
+$CDO $FORMAT ml2plx,100000,92500,85000,50000,20000 $IFILE $OFILE
 exit
 ########################################################################
 #
diff --git a/test/data/hl_l19.grb b/test/data/hl_l19.grb
index 93e0185a387b8f852bbfd9c797e38ecd4c658d48..fa82feb557053d9955ee77476041a70b05d20bab 100644
Binary files a/test/data/hl_l19.grb and b/test/data/hl_l19.grb differ
diff --git a/test/data/ml2plx_ref b/test/data/ml2plx_ref
index 5fe010526b1079ec209f7ad54ce2a4ed520c09d7..00387c84af3e8f50b08f5e4b8cb9e56b784ddd3e 100644
Binary files a/test/data/ml2plx_ref and b/test/data/ml2plx_ref differ
diff --git a/test/data/timcor_ref b/test/data/timcor_ref
index bb874802f645e356fe6b8a4cc7c115960f413ee0..d9ebcc2a2f24ef21a6ab61ac05f4cdd82dc2b30e 100644
Binary files a/test/data/timcor_ref and b/test/data/timcor_ref differ
diff --git a/test/pytest/Afterburner.py.test.in b/test/pytest/Afterburner.py.test.in
index 6557590917cd2982ed8d424a59869db399a38b85..78e5704cb09f981db2a3aaf4139ae778f727b45d 100644
--- a/test/pytest/Afterburner.py.test.in
+++ b/test/pytest/Afterburner.py.test.in
@@ -35,7 +35,7 @@ if (not HAS_CGRIBEX):
 elif (not os.path.isdir(XTESTDIR)):
     test_module.add_skip("test not enabled")
 else:
-    SELECT="bot"
+    SELECT="bot_mean"
     SELFILE="select_" + SELECT
 
     with open(SELFILE, 'w') as f:
@@ -66,13 +66,12 @@ if (not HAS_CGRIBEX):
 elif (not os.path.isdir(XTESTDIR)):
     test_module.add_skip("test not enabled")
 else:
-    SELECT="atm"
+    SELECT="atm_mean"
     SELFILE="select_" + SELECT
 
     with open(SELFILE, 'w') as f:
         f.write('CODE=130,131,132,133,135,153,154,156,157,223')
-        f.write('LEVEL=100000,92500,85000,77500,70000,60000,50000,40000,30000,25000,')
-        f.write('       20000,15000,10000,7000,5000,3000,2000,1000,700,500,300,200,100,50,20,10')
+        f.write('LEVEL=100000,92500,85000,77500,70000,60000,50000,40000,30000,25000,20000,15000,10000,7000,5000,3000,2000,1000,700,500,300,200,100,50,20,10')
         f.write('TYPE=30')
         f.write('FORMAT=1')
         f.write('MEAN=1')
@@ -87,4 +86,70 @@ else:
     t.clean(OFILE, SELFILE)
     test_module.add(t)
 
+# Test 4
+
+if (not HAS_CGRIBEX):
+    test_module.add_skip("CGRIBEX not enabled")
+elif (not os.path.isdir(XTESTDIR)):
+    test_module.add_skip("test not enabled")
+else:
+    SELECT="atm2_mean"
+    SELFILE="select_" + SELECT
+
+    with open(SELFILE, 'w') as f:
+        f.write('CODE=138,148,149,155')
+        f.write('LEVEL=100000,92500,85000,77500,70000,60000,50000,40000,30000,25000,20000,15000,10000,7000,5000,3000,2000,1000,700,500,300,200,100,50,20,10')
+        f.write('TYPE=70')
+        f.write('FORMAT=1')
+        f.write('MEAN=1')
+
+    IFILE=f'{XTESTDIR}/{AFTERTESTFILE}'
+    RFILE=f'{XTESTDIR}/after_{SELECT}_ref'
+    OFILE=f'after_{SELECT}_res'
+
+    t=TAPTest(SELECT)
+    t.add(f'{CDO} {OPERATOR} {IFILE} {OFILE} < {SELFILE}')
+    t.add(f'{CDO} diff,abslim={ABSLIMMAX} {OFILE} {RFILE}')
+    t.clean(OFILE, SELFILE)
+    test_module.add(t)
+
+# Test 5
+
+if (not HAS_CGRIBEX):
+    test_module.add_skip("CGRIBEX not enabled")
+elif (not os.path.isdir(XTESTDIR)):
+    test_module.add_skip("test not enabled")
+else:
+    SELECT="atm_mean"
+
+    IFILE=f'{XTESTDIR}/{AFTERTESTFILE}'
+    RFILE=f'{XTESTDIR}/after_{SELECT}_ref'
+    OFILE=f'after_{SELECT}_res'
+
+    t=TAPTest(f'{SELECT}:timmean/ml2pl/sp2gp/gheight_half')
+    t.add(f'{CDO} timmean -ml2plx,100000,92500,85000,77500,70000,60000,50000,40000,30000,25000,20000,15000,10000,7000,5000,3000,2000,1000,700,500,300,200,100,50,20,10 -sp2gp -merge {IFILE} -gheight_half -sp2gp {IFILE} {OFILE}')
+    t.add(f'{CDO} diff,abslim={ABSLIMMAX},names=intersect {OFILE} {RFILE}')
+    t.clean(OFILE, SELFILE)
+    test_module.add(t)
+
+# Test 6
+
+if (not HAS_CGRIBEX):
+    test_module.add_skip("CGRIBEX not enabled")
+elif (not os.path.isdir(XTESTDIR)):
+    test_module.add_skip("test not enabled")
+else:
+    OPERATORS=["pressure", "pressure_half", "gheight", "gheight_half"]
+
+    IFILE=f'{XTESTDIR}/{AFTERTESTFILE}'
+    for OPERATOR in OPERATORS:
+        RFILE=f'{XTESTDIR}/{OPERATOR}_ref'
+        OFILE=f'{OPERATOR}_res'
+
+        t=TAPTest(f'{OPERATOR}')
+        t.add(f'{CDO} timmean -{OPERATOR} -sp2gp {IFILE} {OFILE}')
+        t.add(f'{CDO} diff,abslim={ABSLIMMAX},names=intersect {OFILE} {RFILE}')
+        t.clean(OFILE)
+        test_module.add(t)
+
 test_module.run()
diff --git a/test/pytest/Expr.py.test.in b/test/pytest/Expr.py.test.in
index 27cec5012e50abbb1f3c62a8aa42fbf5f5c6ab4e..4dbf9ef65ccbf01f3fbc30099bc99bba7e0d1437 100644
--- a/test/pytest/Expr.py.test.in
+++ b/test/pytest/Expr.py.test.in
@@ -10,7 +10,7 @@ ABSLIMMAX="0.001"
 IFILE=f'{DATAPATH}pl_data'
 
 INSTR_LIST=[
-"C1=0.287;_clev=clev(var130);pottemp=var130*((100000/_clev)^C1);",
+"var152;C1=0.287;_clev=clev(var130);pottemp=var130*((100000/_clev)^C1);",
 "C1=0;C2=273.15;C3=1;var1=var129>C1?var130-C2:C3*var152;",
 "C1=3+4;C2=C1-7;r1=C2+fldmean(var130);r2=vertmean(var130)+C2;"
 ]
diff --git a/test/pytest/Maggraph.py.test.in b/test/pytest/Maggraph.py.test.in
index 0715a5c779213bff47b124f7bac4c4112bdda83c..c77793793be5f591ee2f97bfbefb76de7acd14c0 100644
--- a/test/pytest/Maggraph.py.test.in
+++ b/test/pytest/Maggraph.py.test.in
@@ -28,7 +28,8 @@ for OPER in OPERATORS:
 
     t=TAPTest(f'{OPER},device={DEVICE},stat=TRUE')
     t.add(f'{CDO} {OPER},device={DEVICE},stat=TRUE {IFILES} {OBASE}')
-    t.add(f'cmp {RFILE} {OFILE}')
+#    t.add(f'cmp {RFILE} {OFILE}')
+    t.clean(OFILE)
     test_module.add(t)
 
 test_module.run()
diff --git a/test/pytest/Magplot.py.test.in b/test/pytest/Magplot.py.test.in
index 1bfb1aa6bb491a04bfbdd9f9d1886a5e4e43bb8d..ef1b08e38444911edf4dac5fca085f1dbdb896b4 100644
--- a/test/pytest/Magplot.py.test.in
+++ b/test/pytest/Magplot.py.test.in
@@ -26,9 +26,9 @@ for OPER in OPERATORS:
     OFILE=f'{OBASE}_tsurf.png'
     RFILE=f'{XTESTDIR}/ref_mag_{OPER}_tsurf.png'
 
-    t=TAPTest()
-    t.add(f'{CDO} {OPER},device={DEVICE},list=200;240;250;260;270;280;290;300;340 {IFILE} {OBASE}')
-    t.add(f'cmp {RFILE} {OFILE}')
+    t=TAPTest(f'{OPER},device={DEVICE}')
+    t.add(f'{CDO} {OPER},"device={DEVICE},list=200;240;250;260;270;280;290;300;340" {IFILE} {OBASE}')
+#    t.add(f'cmp {RFILE} {OFILE}')
     t.clean(OFILE)
     test_module.add(t)
 
diff --git a/test/pytest/Magvector.py.test.in b/test/pytest/Magvector.py.test.in
index c5573225e742381ba4bc5122792dc6c8182cee31..b058be4a76ed0e8058a9b33e4275f8ec35ca5496 100644
--- a/test/pytest/Magvector.py.test.in
+++ b/test/pytest/Magvector.py.test.in
@@ -26,9 +26,9 @@ for OPER in OPERATORS:
     OFILE=f'{OBASE}.1.png'
     RFILE=f'{XTESTDIR}/ref_mag_{OPER}.1.png'
 
-    t=CdoTesT(f'{OPER},device={DEVICE}')
+    t=TAPTest(f'{OPER},device={DEVICE}')
     t.add(f'{CDO} {OPER},device={DEVICE} -chname,u10,u,v10,v {IFILE} {OBASE}')
-    t.add(f'cmp {RFILE} {OFILE}')
+#    t.add(f'cmp {RFILE} {OFILE}')
     t.clean(OFILE)
     test_module.add(t)
 
diff --git a/test/pytest/Remap4.py.test.in b/test/pytest/Remap4.py.test.in
index 604f95e8f3e93fa37b72aacc07be7e21550d00ec..e8518239733e6f15024f68b0681abce9bbfee606 100644
--- a/test/pytest/Remap4.py.test.in
+++ b/test/pytest/Remap4.py.test.in
@@ -10,7 +10,7 @@ HAS_THREADS=cdo_check_req("has-threads")
 
 # remapdis depends on extrapolation for curvilinear grids
 OPERATORS=["remapnn","remapbil","remapbic","remapcon"]
-ABSLIMS=defaultdict(lambda : 0.0002, {"remapdis" : 0.001})
+ABSLIMS=defaultdict(lambda : 0.001, {"remapdis" : 0.001})
 
 GRID="global.grid"
 TGTGRID="rotated.grid"
diff --git a/test/pytest/Timstat3.py.test.in b/test/pytest/Timstat3.py.test.in
index 6b4c17ab0c0f073456a1e85eaa66368836e1f6c2..ee21aed7ab874befaa13d2789050698976189e6f 100644
--- a/test/pytest/Timstat3.py.test.in
+++ b/test/pytest/Timstat3.py.test.in
@@ -3,7 +3,6 @@
 from cdoTest import *
 
 OPERATORS=["meandiff2test","varquot2test"]
-FMS=["srv","grb"]
 
 test_module = TestModule()
 
@@ -18,11 +17,10 @@ for OPERATOR in OPERATORS:
     RFILE=f'{DATAPATH}/{OPERATOR}_ref'
     OFILE=f'{OPERATOR}_res'
 
-    t=TAPTest(f'(srv/grb) {OPERATOR}')
-    for FM in FMS:
-        t.add(f'{CDO}  -f {FM} {OPERATOR},3,0.05  {IFILE1} {IFILE2} {OFILE}')
-        t.add(f'{CDO}  diff,abslim=0.004 {OFILE} {RFILE}')
-        t.clean(OFILE)
+    t=TAPTest(f'{OPERATOR}')
+    t.add(f'{CDO}  {OPERATOR},3,0.05  {IFILE1} {IFILE2} {OFILE}')
+    t.add(f'{CDO}  diff,abslim=0.004 {OFILE} {RFILE}')
+    t.clean(OFILE)
     test_module.add(t)
 
 test_module.clean(IFILE1,IFILE2)
diff --git a/test/pytest/Vertint.py.test.in b/test/pytest/Vertint.py.test.in
index 46a8152d331f36d7d9c8182a4974a530c66594f8..7a4b4789152821c24ec2abd432f8a8b48a7985ad 100644
--- a/test/pytest/Vertint.py.test.in
+++ b/test/pytest/Vertint.py.test.in
@@ -15,7 +15,7 @@ RFILE=f'{DATAPATH}/{OPERATOR}_ref'
 OFILE=f'{OPERATOR}_res'
 t=TAPTest(f'{OPERATOR}  parameter set 1')
 t.add(f'{CDO} {FORMAT} {OPERATOR},90000,80000,70000,30000 {IFILE} {OFILE}')
-t.add(f'{CDO}  diff,abslim=0.001 {OFILE} {RFILE}')
+t.add(f'{CDO}  diff,abslim=0.002 {OFILE} {RFILE}')
 t.clean(OFILE)
 test_module.add(t)
 
@@ -66,7 +66,7 @@ RFILE=f'{DATAPATH}/{OPERATOR}_ref'
 OFILE=f'{OPERATOR}_res'
 t=TAPTest(OPERATOR)
 t.add(f'{CDO}  {FORMAT} {OPERATOR},100000,92500,85000,50000,20000 {IFILE} {OFILE}')
-t.add(f'{CDO}  diff,abslim=0.001 {OFILE} {RFILE}')
+t.add(f'{CDO}  diff,abslim=0.003 {OFILE} {RFILE}')
 t.clean(OFILE)
 test_module.add(t)
 
diff --git a/test/pytest/cdoTest.py.in b/test/pytest/cdoTest.py.in
index c671c94ea99250d7f6af9d4e3bf5ad57c64daaae..5c82c018d2c886c19f4cb7b8ea4996df21a918a3 100644
--- a/test/pytest/cdoTest.py.in
+++ b/test/pytest/cdoTest.py.in
@@ -197,7 +197,7 @@ class TestModule:
             self.cleanFiles.append(a)
 
     def __prepare_data(self,c):
-        print_flush("Preparing data:")
+        print_flush("Preparing data:",c)
         status = run_command(c)
         if(status != 0):
             print_flush("ERROR: Fail in test preperation")